我的编程空间,编程开发者的网络收藏夹
学习永远不晚

redisson分布式限流RRateLimiter怎么使用

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

redisson分布式限流RRateLimiter怎么使用

今天小编给大家分享一下redisson分布式限流RRateLimiter怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

分布式限流-单位时间多实例多线程访问次数限制

1.简单使用

 public static void main(String[] args) throws InterruptedException {        RRateLimiter rateLimiter = createLimiter();        int allThreadNum = 20;        CountDownLatch latch = new CountDownLatch(allThreadNum);        long startTime = System.currentTimeMillis();        for (int i = 0; i < allThreadNum; i++) {//            new Thread(() -> {            if(i % 3 == 0) Thread.sleep(1000);            boolean pass = rateLimiter.tryAcquire();            if(pass) {                log.info("get ");            } else {                log.info("no");            }//          latch.countDown();//            }).start();        }//        latch.await();        System.out.println("Elapsed " + (System.currentTimeMillis() - startTime));    }    public static RRateLimiter createLimiter() {        Config config = new Config();        config.useSingleServer()                .setTimeout(1000000)                .setPassword("123456")                .setAddress("redis://xxxx:6379");        RedissonClient redisson = Redisson.create(config);        RRateLimiter rateLimiter = redisson.getRateLimiter("myRateLimiter3");        // 初始化:PER_CLIENT 单实例执行,OVERALL 全实例执行        // 最大流速 = 每10秒钟产生3个令牌        rateLimiter.trySetRate(RateType.OVERALL, 3, 10, RateIntervalUnit.SECONDS);        return rateLimiter;    }

实际结果:

[2022-10-29 14:32:46.261][INFO ][main][][] RedisTest - get 
[2022-10-29 14:32:46.312][INFO ][main][][] RedisTest - get 
[2022-10-29 14:32:46.358][INFO ][main][][] RedisTest - get 
[2022-10-29 14:32:47.416][INFO ][main][][] RedisTest - no
[2022-10-29 14:32:47.469][INFO ][main][][] RedisTest - no
[2022-10-29 14:32:47.517][INFO ][main][][] RedisTest - no
[2022-10-29 14:32:48.577][INFO ][main][][] RedisTest - no
[2022-10-29 14:32:48.623][INFO ][main][][] RedisTest - no

2. 实现限流redisson使用了哪些redis数据结构

  • Hash结构 -- 限流器结构:

参数rate代表速率

参数interval代表多少时间内产生的令牌

参数type代表单机还是集群

redisson分布式限流RRateLimiter怎么使用

  • ZSET结构 -- 记录获取令牌的时间戳,用于时间对比。

1667025166312 --> 2022-10-29 14:32:46

1667025166262 --> 2022-10-29 14:32:46

1667025166215 --> 2022-10-29 14:32:46

redisson分布式限流RRateLimiter怎么使用

  • String结构 --记录的是当前令牌桶中的令牌数【很明显被我用完了现在是0】

redisson分布式限流RRateLimiter怎么使用

3. 超过10s,我再次获取一个令牌,数据结构发生的变化

  • ZSET结构。-- 新生成一个ZSET结构,存放获取令牌的时间戳

redisson分布式限流RRateLimiter怎么使用

  • String 结构 --当前令牌桶还有2个令牌

redisson分布式限流RRateLimiter怎么使用

4. 源码浅析

RRateLimiter rateLimiter = redisson.getRateLimiter("myRateLimiter3");// 初始化// 最大流速 = 每10秒钟产生3个令牌rateLimiter.trySetRate(RateType.PER_CLIENT, 3, 10, RateIntervalUnit.SECONDS);

初始化定义没有什么好讲的,就是创建HASH结构

主要还是讲讲: rateLimiter.tryAcquire()

private <T> RFuture<T> tryAcquireAsync(RedisCommand<T> command, Long value) {    return this.commandExecutor.evalWriteAsync(        this.getName(), LongCodec.INSTANCE, command,         "local rate = redis.call('hget', KEYS[1], 'rate');        local interval = redis.call('hget', KEYS[1], 'interval');        local type = redis.call('hget', KEYS[1], 'type');        assert(            rate ~= false and interval ~= false and type ~= false,  'RateLimiter is not initialized'            )            local valueName = KEYS[2];            local permitsName = KEYS[4];            if type == '1' then valueName = KEYS[3];            permitsName = KEYS[5];            end;            local currentValue = redis.call('get', valueName);             if currentValue ~= false             then             local expiredValues = redis.call(                'zrangebyscore', permitsName, 0, tonumber(ARGV[2]                )                 - interval                );                local released = 0;                 for i, v in ipairs(expiredValues)                 do local random, permits = struct.unpack('fI', v);                released = released + permits;end;                 if released > 0                 then                 redis.call('zrem', permitsName, unpack(expiredValues));                 currentValue = tonumber(currentValue) + released;                 redis.call('set', valueName, currentValue);                end;                if tonumber(currentValue) < tonumber(ARGV[1])                 then                 local nearest = redis.call(                    'zrangebyscore', permitsName, '(' .. (tonumber(ARGV[2])                     - interval), tonumber(ARGV[2]), 'withscores', 'limit', 0, 1);                     local random, permits = struct.unpack('fI', nearest[1]);                    return tonumber(nearest[2])                     - (tonumber(ARGV[2])                     - interval);                    else                     redis.call('zadd', permitsName, ARGV[2], struct.pack('fI', ARGV[3], ARGV[1]));                     redis.call('decrby', valueName, ARGV[1]);                     return nil;                     end;                     else                     assert(tonumber(rate) >= tonumber(ARGV[1]), 'Requested permits amount could not exceed defined rate');                     redis.call('set', valueName, rate);                     redis.call('zadd', permitsName, ARGV[2], struct.pack('fI', ARGV[3], ARGV[1]));                     redis.call('decrby', valueName, ARGV[1]);                     return nil;                     end;", Arrays.asList(this.getName(),                     this.getValueName(),                     this.getClientValueName(),                     this.getPermitsName(),                     this.getClientPermitsName()),                     new Object[]{value,                     System.currentTimeMillis(),                     ThreadLocalRandom.current().nextLong()                    }                );}

主要就是这段lua代码,下面我详细过一下

作者目前用的3.16.3版本,刚好遇见redisson的bug,见3197,请大家用最新版本,以下为修复后解析。

-- 获取hash结构的速率local rate = redis.call("hget", KEYS[1], "rate")-- 获取hash结构的时间区间(ms)local interval = redis.call("hget", KEYS[1], "interval")-- 获取hash结构的时间类型local type = redis.call("hget", KEYS[1], "type")-- 判断是否初始化限流结构assert(rate ~= false and interval ~= false and type ~= false, "RateLimiter is not initialized")-- {name}:value string结构,这个key记录的是当前令牌桶中的令牌数local valueName = KEYS[2]-- {name}:permits zset结构,记录了请求的令牌数,score则为请求的时间戳local permitsName = KEYS[4]-- 单机限流才会用到,集群模式不用关注if type == "1" then    valueName = KEYS[3]    permitsName = KEYS[5]end-- 生产速率rate必须比请求的令牌数大assert(tonumber(rate) >= tonumber(ARGV[1]), "Requested permits amount could not exceed defined rate")-- 初始化RateLimiter并不会初始化stirng结构,因此第一次获取这里currentValue是nulllocal currentValue = redis.call("get", valueName)if currentValue ~= false then    -- 第二次获取令牌执行    -------------------------- 获取zset结构:统计之前的请求令牌数    -- 范围是0 ~ (第二次请求时间戳 - 令牌生产的时间)    local expiredValues = redis.call("zrangebyscore", permitsName, 0, tonumber(ARGV[2]) - interval)    local released = 0    -- lua迭代器,遍历expiredValues,如果有值,那么released等于之前所有请求的令牌数之和,表示应该释放多少令牌    for i, v in ipairs(expiredValues) do        -- 获取请求数permits        local random, permits = struct.unpack("fI", v)        released = released + permits    end    -- 之前的请求令牌数 > 0, 例如10s产生3个令牌,现在超过10s了,重置周期并计算剩余令牌数    if released > 0 then        -- 移除zset中所有元素【要求是同一个限流器permitsName,不然就移除不了,尴尬】         redis.call("zrem", permitsName, unpack(expiredValues))        currentValue = tonumber(currentValue) + released        ------------------------- 更新string结构:=剩下令牌数+释放令牌数        redis.call("set", valueName, currentValue)    end    -- 如果当前令牌数 < 请求的令牌数    if tonumber(currentValue) < tonumber(ARGV[1]) then        -- 从zset中找到距离当前时间最近的那个请求,也就是上一次放进去的请求信息        local nearest = redis.call('zrangebyscore', permitsName, '(' .. (tonumber(ARGV[2]) - interval), tonumber(ARGV[2]), 'withscores', 'limit', 0, 1);         local random, permits = struct.unpack("fI", nearest[1])        -- 返回 上一次请求的时间戳 - (当前时间戳 - 令牌生成的时间间隔) 这个值表示还需要多久才能生产出足够的令牌        return tonumber(nearest[2]) - (tonumber(ARGV[2]) - interval)    else        -- 如果当前令牌数 ≥ 请求的令牌数,表示令牌够多,更新zset        ------------------------- 更新zset结构        redis.call("zadd", permitsName, ARGV[2], struct.pack("fI", ARGV[3], ARGV[1]))        ------------------------- 更新Stringt结构,减少一个剩下的令牌数        redis.call("decrby", valueName, ARGV[1])        return nil    endelse    --------汀雨笔记----------------- 初始化Stringt结构,当前限流器的令牌数    redis.call("set", valueName, rate)    --------汀雨笔记----------------- 初始化zset结构    redis.call("zadd", permitsName, ARGV[2], struct.pack("fI", ARGV[3], ARGV[1]))    -- struct.pack第一个参数表示格式字符串,f是浮点数、I是长整数。所以这个格式字符串表示的是把一个浮点数和长整数拼起来的结构体,    -- ARGV[2]就是请求时间戳,ARGV[1]是请求的令牌数,统计会用到,ARGV[3]是当前时间戳为种子的随机数,具体用处还不知道,知道的网友可以留言    ------------------------- 更新Stringt结构,因为这是获取令牌操作,减掉一个令牌    -------------------------【本文作者认为,这里可以直接初始化string结构,值为rate - 1】    redis.call("decrby", valueName, ARGV[1])    return nilend

这段lua代码也并不复杂,令牌桶的数量主要是通过时间窗口来控制,判断上一个请求是否超过了令牌生产周期。

留下一个疑问?

-- 移除zset中所有元素【要求是同一个限流器permitsName,不然就移除不了,尴尬】 redis.call("zrem", permitsName, unpack(expiredValues))

我自己在本地测试,只要超过10s,permitsName就不一样,这就导致了这部分数据是不能移除的,就产生了冗余数据,从前面的截图也可以看出,是新生成了一个zset数据结构。

相当于直接走到了这一步:

------------------------- 更新zset结构 redis.call("zadd", permitsName, ARGV[2], struct.pack("fI", ARGV[3], ARGV[1]))

以上就是“redisson分布式限流RRateLimiter怎么使用”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网行业资讯频道。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

redisson分布式限流RRateLimiter怎么使用

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

redisson分布式限流RRateLimiter怎么使用

今天小编给大家分享一下redisson分布式限流RRateLimiter怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧
2023-07-04

redisson分布式限流RRateLimiter源码解析

这篇文章主要为大家介绍了redisson分布式限流RRateLimiter源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-13

Redisson分布式限流的实现原理解析

目录正文RRateLimiter使用RRateLimiter的实现RRateLimiter使用时注意事项RRateLimiter是非公平限流器Rate不要设置太大限流的上限取决于Redis单实例的性能分布式限流的本质正文我们目前在工作中遇到
2023-02-12

SpringBoot怎么整合分布式锁redisson

这篇文章主要介绍“SpringBoot怎么整合分布式锁redisson”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“SpringBoot怎么整合分布式锁redisson”文章能帮助大家解决问题。1、
2023-07-05

kubernetes怎么实现分布式限流

本篇内容主要讲解“kubernetes怎么实现分布式限流”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“kubernetes怎么实现分布式限流”吧!一、概念限流(Ratelimiting)指对应用
2023-06-29

Redisson分布式闭锁RCountDownLatch如何使用

这篇文章主要介绍了Redisson分布式闭锁RCountDownLatch如何使用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Redisson分布式闭锁RCountDownLatch如何使用文章都会有所收获,
2023-07-05

Redisson分布式信号量RSemaphore如何使用

本文小编为大家详细介绍“Redisson分布式信号量RSemaphore如何使用”,内容详细,步骤清晰,细节处理妥当,希望这篇“Redisson分布式信号量RSemaphore如何使用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一
2023-07-05

Redisson中怎么实现Redis分布式锁

Redisson中怎么实现Redis分布式锁,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。Redis几种架构Redis发展到现在,几种常见的部署架构有:单机模式;主从模式;
2023-06-20

如何正确的使用redisson分布式锁

这期内容当中小编将会给大家带来有关如何正确的使用redisson分布式锁,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。Redisson是Redis官方推荐的Java版的Redis客户端。它提供的功能非常多
2023-06-14

利用Redis实现分布式限流

标题:利用Redis实现分布式限流正文:随着互联网的快速发展,网站和服务的并发访问量不断增加,为了保护后端系统的稳定性,限制并发访问量成为了一项重要的任务。在分布式系统中,为了保证多个服务实例之间的共享状态,我们可以使用Redis作为分布式
利用Redis实现分布式限流
2023-11-07

如何使用Redis实现分布式限流功能

如何使用Redis实现分布式限流功能引言:随着互联网的快速发展,业务系统的访问量也日益增加。当流量集中到某一业务系统时,会给系统的稳定性和性能带来一定的威胁。为了保护业务系统,限流成为一种必不可少的手段。在分布式系统中,使用Redis可以方
如何使用Redis实现分布式限流功能
2023-11-07

Redisson分布式闭锁RCountDownLatch的使用详细讲解

分布式锁和我们java基础中学习到的synchronized略有不同,synchronized中我们的锁是个对象,当前系统部署在不同的服务实例上,单纯使用synchronized或者lock已经无法满足对库存一致性的判断。本次主要讲解基于rediss实现的分布式锁
2023-02-11

Spring Cache怎么使用Redisson分布式锁解决缓存击穿问题

本篇内容主要讲解“Spring Cache怎么使用Redisson分布式锁解决缓存击穿问题”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Spring Cache怎么使用Redisson分布式锁解
2023-06-30

Java-Redis-Redisson分布式锁的功能使用及实现

这篇文章主要介绍了Java-Redis-Redisson-分布式锁的功能使用及实现,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
2022-11-13

Redisson分布式信号量RSemaphore的使用超详细讲解

这篇文章主要介绍了Redisson分布式信号量RSemaphore的使用,基于Redis的Redisson的分布式信号量RSemaphore采用了与java.util.concurrent.Semaphore相似的接口和用法
2023-02-11

如何使用自定义注解实现redisson分布式锁

这篇文章主要讲解了“如何使用自定义注解实现redisson分布式锁”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何使用自定义注解实现redisson分布式锁”吧!自定义注解实现rediss
2023-06-29

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录