Redisson 限流器源码分析

Redisson 限流器源码分析

对上篇文章网友评论给出问题进行解答:redis 的key 是否会过期

可以先阅读上篇文章:

redis + AOP + 自定义注解实现接口限流 - 古渡蓝按 - 博客园 (cnblogs.com)

注解AOP 代码部分提取

java 复制代码
// 调用Reids工具类的rateLimiter 方法
 long number = RedisUtils.rateLimiter(combineKey, rateType, count, time);

redis 工具类

java 复制代码
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 方法

java 复制代码
@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 复制代码
-- 源码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进行失效时间设置

修改后代码:

java 复制代码
/**
     * 限流
     *
     * @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;
    }
}

如果代码写的有问题,欢迎大家评论交流,进行指点!!!

也希望大家点个关注哦~~~~~~~~

相关推荐
邂逅星河浪漫21 小时前
【RabbitMQ】消息队列·详解+实操演示+功能实现(微服务架构)
rabbitmq·springboot·springamqp·consumer·exchange·producer·queue
vortex52 天前
用 Scoop 快速部署 JeecgBoot 开发环境:从依赖安装到服务管理
java·windows·springboot·web·开发·jeecg-boot
javpy5 天前
为什么Service层和Mapper层需要实现interface接口
java·springboot
古渡蓝按6 天前
基于 Word 模板占位符的动态文档生成实践(源码+保姆版)
springboot
带刺的坐椅6 天前
Solon (可替换 SpringBoot)集成 Docker 实战:30分钟搞定轻量级应用容器化部署
java·docker·jar·springboot·solon
程序员杨工7 天前
【原创】SpringBoot3+Vue3客户管理系统
vue.js·springboot
bug攻城狮8 天前
SaaS多租户架构实践:字段隔离方案(共享数据库+共享Schema)
mysql·架构·mybatis·springboot·1024程序员节
后端小张11 天前
【JAVA 进阶】SpringBoot集成Sa-Token权限校验框架深度解析
java·spring boot·spring·架构·sa-token·springboot·权限框架
梁辰兴12 天前
企业培训笔记:外卖平台后端--套餐管理模块--新建套餐信息
笔记·vue·mybatis·springboot·外卖管理系统
No8g攻城狮12 天前
【异常解决】使用DateUtil.isSameDay()方法判断秒级时间戳是否属于同一天踩过的坑
java·jvm·spring boot·java-ee·springboot