工具类
public class RedisUtils {
private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class);
/**
* 限流
*
* @param key 限流key
* @param rateType 限流类型
* @param rate 速率
* @param rateInterval 速率间隔
* @return -1 表示失败
*/
public static long rateLimiter(String key, RateType rateType, int rate, int rateInterval) {
// 获取一个限流器
RRateLimiter rateLimiter = CLIENT.getRateLimiter(key);
// 将限流的配置信息保存在Redis中
rateLimiter.trySetRate(rateType, rate, rateInterval, RateIntervalUnit.SECONDS);
// tryAcquire 用于获取当前可用的许可数
if (rateLimiter.tryAcquire()) {
return rateLimiter.availablePermits();
} else {
return -1L;
}
}
}
解析
rateLimiter.trySetRate(rateType, rate, rateInterval, RateIntervalUnit.SECONDS);
源码分析
源码截图:
分析:trySetRate 调用 trySetRateAsync 方法
@Override
public boolean trySetRate(RateType type, long rate, long rateInterval, RateIntervalUnit unit) {
return get(trySetRateAsync(type, rate, rateInterval, unit));
}
@Override
public RFuture<Boolean> trySetRateAsync(RateType type, long rate, long rateInterval, RateIntervalUnit unit) {
return commandExecutor.evalWriteNoRetryAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"redis.call('hsetnx', KEYS[1], 'rate', ARGV[1]);"
+ "redis.call('hsetnx', KEYS[1], 'interval', ARGV[2]);"
+ "return redis.call('hsetnx', KEYS[1], 'type', ARGV[3]);",
Collections.singletonList(getRawName()), rate, unit.toMillis(rateInterval), type.ordinal());
}
逐步分析代码:
commandExecutor.evalWriteNoRetryAsync():这里使用了 Redis 的 EVAL 命令,这个命令允许执行 Lua 脚本,而不会受到 Redis 的同步阻塞操作。
getRawName():这是获取限流器的名称或标识。
RedisCommands.EVAL_BOOLEAN:表示执行 Lua 脚本后期望的返回值类型为 Boolean。
源码lua 脚本解释
– 源码lua 脚本
"redis.call('hsetnx', KEYS[1], 'rate', ARGV[1]);"
+ "redis.call('hsetnx', KEYS[1], 'interval', ARGV[2]);"
+ "return redis.call('hsetnx', KEYS[1], 'type', ARGV[3]);"
--- 解释
这段 Lua 脚本中,通过 redis.call('hsetnx', KEYS[1], 'rate', ARGV[1]) 等命令,尝试对 Redis 的 Hash 数据结构进行设置操作。
首先尝试设置 'rate' 字段为传入的速率值;
然后尝试设置 'interval' 字段为传入的时间间隔值;
最后尝试设置 'type' 字段为传入的类型值。这里使用了 hsetnx 命令来进行设置操作,如果字段已存在,则不会进行设置操作。
Collections.singletonList(getRawName()):将限流器的名称作为参数传递给 Lua 脚本。
rate, unit.toMillis(rateInterval), type.ordinal():这三个参数分别是速率、时间间隔以毫秒为单位、以及限流类型
总结:这段代码本身并没有提供设置限流器自动过期的功能。在 Redisson 中,限流器自动过期的功能通常不是默认包含在限流器的设置中。
二、设置限流器的失效时间
限流器自动过期(是指的是限流这个功能),可以使用expire进行失效时间设置
修改后代码:
/**
* 限流
*
* @param key 限流key
* @param rateType 限流类型
* @param rate 速率
* @param rateInterval 速率间隔
* @param expirationTimeInSeconds 过期时间(秒)
* @param isExpire 是否设置限流器过期
* @return -1 表示失败
*/
public static long rateLimiter(String key, RateType rateType, int rate, int rateInterval, long expirationTimeInSeconds,boolean isExpire) {
RRateLimiter rateLimiter = CLIENT.getRateLimiter(key);
rateLimiter.trySetRate(rateType, rate, rateInterval, RateIntervalUnit.SECONDS);
if(isExpire){
// 是否设置过期时间
rateLimiter.expire(expirationTimeInSeconds, TimeUnit.SECONDS);
}
if (rateLimiter.tryAcquire()) {
return rateLimiter.availablePermits();
} else {
return -1L;
}
}