Redisson 限流与高级特性
一、RRateLimiter(分布式限流器)
Redisson 的限流器基于令牌桶算法,支持分布式场景下的平滑限流。
基本用法
RRateLimiter limiter = redisson.getRateLimiter("rateLimiter:order");
// 初始化:每秒 10 个令牌(速率 = 10/s)
limiter.trySetRate(RateType.OVERALL, 10, 1, RateIntervalUnit.SECONDS);
// 获取令牌(阻塞直到有令牌)
limiter.acquire();
// 尝试获取令牌(非阻塞)
boolean acquired = limiter.tryAcquire();
boolean acquired = limiter.tryAcquire(5); // 尝试获取 5 个令牌
boolean acquired = limiter.tryAcquire(1, 10, TimeUnit.SECONDS); // 最多等 10 秒
// 获取等待时间
long waitTime = limiter.availablePermits();
RateType 说明
// 跨所有实例共享限流(全局限流)
RateType.OVERALL
// 例如:整个系统的下单接口限流 100/s
// 按客户端实例单独限流
RateType.PER_CLIENT
// 例如:每个 API 网关实例各自限流 100/s
典型场景
@Service
public class OrderService {
@Autowired
private RedissonClient redisson;
@PostConstruct
public void init() {
RRateLimiter limiter = redisson.getRateLimiter("rate:createOrder");
limiter.trySetRate(RateType.OVERALL, 100, 1, RateIntervalUnit.SECONDS);
}
public Result createOrder(OrderDTO dto) {
RRateLimiter limiter = redisson.getRateLimiter("rate:createOrder");
// 等待获取令牌(被限流时友好等待)
limiter.acquire();
// 实际业务
return orderService.createOrder(dto);
}
}
速率设置速查
// 每秒 100 个
limiter.trySetRate(RateType.OVERALL, 100, 1, RateIntervalUnit.SECONDS);
// 每分钟 1000 个
limiter.trySetRate(RateType.OVERALL, 1000, 1, RateIntervalUnit.MINUTES);
// 每小时 5000 个
limiter.trySetRate(RateType.OVERALL, 5000, 1, RateIntervalUnit.HOURS);
二、RPermitExpirableSemaphore vs RRateLimiter
| 特性 | RRateLimiter | RPermitExpirableSemaphore |
|---|---|---|
| 算法 | 令牌桶(平滑限流) | 信号量(固定并发数) |
| 突发流量 | ✅ 允许一定突发 | ❌ 严格按许可数 |
| 等待 | 支持 | 支持 |
| 适用场景 | API 限流、流量整形 | 数据库连接池、并发任务数控制 |
// RRateLimiter:平滑限流
// 配置 10/s,瞬间 100 请求持续约 10s 处理完
RRateLimiter rl = redisson.getRateLimiter("api:rate");
// RSemaphore:控制最大并发
// 最多 10 个同时在执行
RSemaphore sp = redisson.getSemaphore("pool:concurrent");
三、RMapCache(带缓存的 Map)
RMapCache<String, Object> cache = redisson.getMapCache("myCache");
// 每个字段有自己的 TTL
cache.put("key1", "value1", 10, TimeUnit.MINUTES);
cache.put("key2", "value2", 1, TimeUnit.HOURS);
// 最大空闲时间
cache.put("key3", "value3", 10, TimeUnit.MINUTES, 5, TimeUnit.MINUTES);
// TTL: 10 分钟,空闲 5 分钟未访问则自动清除
// 基于比较的写入
cache.putIfAbsent("key4", "value4", 30, TimeUnit.SECONDS);
RLocalCachedMap(两级缓存)
LocalCachedMapOptions options = LocalCachedMapOptions.defaults()
.evictionPolicy(LocalCachedMapOptions.EvictionPolicy.LFU) // LFU 淘汰策略
.cacheSize(1000) // 本地缓存最多 1000 个
.timeToLive(60, TimeUnit.SECONDS) // 本地缓存 TTL
.syncStrategy(LocalCachedMapOptions.SyncStrategy.INVALIDATE) // 本地缓存失效策略
.storeMode(LocalCachedMapOptions.StoreMode.LOCAL_CACHE_REDIS); // 本地+远程
RLocalCachedMap<String, Object> cache = redisson.getLocalCachedMap("hot:data", options);
| 策略 | 说明 |
|---|---|
LFU | 最近最少使用 |
LRU | 最近最久未使用 |
SOFT | 软引用(JVM GC 时回收) |
WEAK | 弱引用(每次 GC 时回收) |
NONE | 不移除 |
| StoreMode | 行为 |
|---|---|
LOCAL_CACHE_REDIS | 本地缓存 + Redis 双写 |
LOCAL_CACHE | 仅本地缓存(不同步 Redis 变更) |
REDIS_ONLY | 仅 Redis(禁用本地缓存) |
四、RTransaction(分布式事务)
Redisson 支持在 Redis 上执行分布式事务:
RTransaction transaction = redisson.createTransaction(
TransactionOptions.defaults()
.timeout(30, TimeUnit.SECONDS)
.responseTimeout(30, TimeUnit.SECONDS)
);
try {
RMap<String, String> map = transaction.getMap("myMap");
map.put("key1", "value1");
RAtomicLong counter = transaction.getAtomicLong("counter");
counter.incrementAndGet();
transaction.commit();
} catch (Exception e) {
transaction.rollback();
}
注意:这是 Redis 层面的事务(WATCH/MULTI/EXEC 的封装),非 ACID 关系型事务。
五、RJsonStore(JSON 存储 + 查询)
Redisson 支持对 JSON 文档进行查询(需 RedisJSON 模块):
RJsonStore store = redisson.getJsonStore("json:store");
// 存储
store.set("$.user", new User("张三", 25));
// 路径读取
String name = store.get("$.user.name");
Integer age = store.get("$.user.age");
六、RBatch(批量操作——Pipeline)
RBatch batch = redisson.createBatch(
BatchOptions.defaults()
.executionMode(BatchOptions.ExecutionMode.REDIS_READ_ATOMIC)
.responseTimeout(30, TimeUnit.SECONDS)
.retryInterval(1000)
);
// 添加批量命令
RFuture<String> getFuture = batch.getBucket("key1").getAsync();
RFuture<Boolean> setFuture = batch.getBucket("key2").setAsync("value2");
// 一次性执行(Pipeline)
BatchResult result = batch.execute();
// 获取结果
String val1 = getFuture.get();
七、Redisson 与 Spring Cache 集成
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager(RedissonClient redisson) {
Map<String, CacheConfig> config = new HashMap<>();
// 用户缓存:TTL 10 分钟
config.put("users", new CacheConfig(10 * 60 * 1000, 0));
// 商品缓存:本地缓存 + Redis
config.put("products", new CacheConfig(60 * 60 * 1000, 0)
.setStoreMode(CacheConfig.StoreMode.LOCAL_CACHE_REDIS));
// 本地缓存配置:最大 1000 个,LFU
LocalCachedMapOptions options = LocalCachedMapOptions.defaults()
.evictionPolicy(LocalCachedMapOptions.EvictionPolicy.LFU)
.cacheSize(1000);
config.put("products", new LocalCachedCacheConfig(60 * 60 * 1000, 0, options));
return new RedissonSpringCacheManager(redisson, config);
}
}
@Service
public class UserService {
@Cacheable(value = "users", key = "#id")
public User getUser(Long id) {
// 从数据库查询
return userRepository.findById(id);
}
@CachePut(value = "users", key = "#user.id")
public User updateUser(User user) {
return userRepository.save(user);
}
@CacheEvict(value = "users", key = "#id")
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
}
八、Redisson 与 Spring 事务集成
@Configuration
public class TransactionConfig {
@Bean
public TransactionManager transactionManager(RedissonClient redisson) {
return new RedissonTransactionManager(redisson);
}
}
@Service
public class OrderService {
@Transactional(transactionManager = "transactionManager")
public void createOrder(Order order) {
// Redisson 分布式事务
// 配合 @Transactional,保证 Redis 操作的事务性
}
}
九、生产环境配置建议
Config config = new Config();
// 线程池配置
config.setNettyThreads(32);
config.setThreads(16);
// 序列化
config.setCodec(new Kryo5Codec());
// 连接池
config.useSingleServer()
.setAddress("redis://localhost:6379")
.setConnectionPoolSize(32) // 连接池大小
.setConnectionMinimumIdleSize(8) // 最小空闲连接
.setIdleConnectionTimeout(10000) // 空闲连接超时
.setConnectTimeout(5000) // 连接超时
.setTimeout(3000) // 命令执行超时
.setRetryAttempts(3) // 重试次数
.setRetryInterval(1500) // 重试间隔
// 可靠监听器
.setPingConnectionInterval(30000) // 心跳间隔
.setKeepPubSubOrder(true); // 保证 Pub/Sub 顺序
// 故障转移
config.setLockWatchdogTimeout(30000); // 锁看门狗超时
十、故障排查
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 获取锁超时 | Redis 连接池满了 | 增大连接池/检查慢查询 |
| 锁不能释放 | 业务异常未 unlock | finally 释放/设置过期时间 |
| 数据不一致 | 序列化导致 | 改用 Kryo/JSON |
| 连接断开 | 网络问题 | 增加重试次数 + 心跳 |
| 内存暴涨 | 未设过期时间 | 设置 TTL / 用 RMapCache |
