Redisson限流器RRateLimiter使用及源码分析

news2024/10/6 10:30:53

在这里插入图片描述

一、使用

使用很简单、如下

// 1、 声明一个限流器
RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);
 
// 2、 设置速率,5秒中产生3个令牌
rateLimiter.trySetRate(RateType.OVERALL, 3, 5, RateIntervalUnit.SECONDS);
 
// 3、试图获取一个令牌,获取到返回true
rateLimiter.tryAcquire(1)

二、原理

1、getRateLimiter

// 声明一个限流器 名称 叫key
redissonClient.getRateLimiter(key)

2、trySetRate

trySetRate 方法跟进去底层实现如下:

@Override
public RFuture<Boolean> trySetRateAsync(RateType type, long rate, long rateInterval, RateIntervalUnit unit) {
    return commandExecutor.evalWriteAsync(getName(), 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.<Object>singletonList(getName()), rate, unit.toMillis(rateInterval), type.ordinal());
}

举个例子,更容易理解:

比如下面这段代码,5 秒中产生 3 个令牌,并且所有实例共享(RateType.OVERALL所有实例共享、RateType.CLIENT单实例端共享)

trySetRate(RateType.OVERALL, 3, 5, RateIntervalUnit.SECONDS);


那么 redis 中就会设置 3 个参数:

hsetnx,key,rate,3
 
hsetnx,key,interval,5
 
hsetnx,key,type,0

接着看tryAcquire(1)方法:底层源码如下

private <T> RFuture<T> tryAcquireAsync(RedisCommand<T> command, Long value) {
    return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,
            "local rate = redis.call('hget', KEYS[1], 'rate');"  //1
          + "local interval = redis.call('hget', KEYS[1], 'interval');"  //2
          + "local type = redis.call('hget', KEYS[1], 'type');" //3
          + "assert(rate ~= false and interval ~= false and type ~= false, 'RateLimiter is not initialized')" //4
          
          + "local valueName = KEYS[2];" //5
          + "if type == 1 then "
              + "valueName = KEYS[3];" //6
          + "end;"
          
          + "local currentValue = redis.call('get', valueName); " //7
          + "if currentValue ~= false then " 
                 + "if tonumber(currentValue) < tonumber(ARGV[1]) then " //8
                     + "return redis.call('pttl', valueName); "
                 + "else "
                     + "redis.call('decrby', valueName, ARGV[1]); " //9
                     + "return nil; "
                 + "end; "
          + "else " //10
                 + "redis.call('set', valueName, rate, 'px', interval); " 
                 + "redis.call('decrby', valueName, ARGV[1]); "
                 + "return nil; "
          + "end;",
            Arrays.<Object>asList(getName(), getValueName(), getClientValueName()), 
            value, commandExecutor.getConnectionManager().getId().toString());
}

第 1、2、3 备注行是获取上一步 set 的 3 个值:rate、interval、type,如果这 3 个值没有设置,直接返回 rateLimiter 没有被初始化。

第 5 备注行声明一个变量叫valueName 值为KEYS[2],KEYS[2]对应的值是getValueName()方法,getValueName()返回的就是上面第一步getRateLimiter我们设置的 key;如果 type=1,表示全局共享,那么valueName 的值改为取KEYS[3]KEYS[3]对应的值为getClientValueName(),查看getClientValueName()源码:

String getClientValueName() {
        return suffixName(getValueName(), commandExecutor.getConnectionManager().getId().toString());
   }

ConnectionManager().getId()如下:

public interface ConnectionManager {
    
    UUID getId();
 
    省略...
}

这个 getId() 是每个客户端初始化的时候生成的 UUID,即每个客户端的 getId 是唯一的,这也就验证了trySetRate方法中RateType.ALLRateType.PER_CLIENT的作用。

  • 接着看第 7 标准行,获取valueName对应的值currentValue;首次获取肯定为空,那么看第 10 标准行 else 的逻辑

  • set valueName 3 px 5,设置 key=valueName value=3 过期时间为 5 秒

  • decrby valueName 1,将上面 valueName 的值减 1

  • 那么如果第二次访问,第 7 标注行返回的值存在,将会走第 8 标注行,紧接着走如下判断

  • 如果当前 valueName 的值也就是 3,小于要获得的令牌数量 (tryAcquire方法中的入参),那么说明当前时间内(key 的有效期 5 秒内),令牌的数量已经被用完,返回 pttl(key 的剩余过期时间);反之说明桶中有足够的令牌,获取之后将会把桶中的令牌数量减 1,至此结束。

总结

redission分布式限流采用令牌桶思想和固定时间窗口,trySetRate方法设置桶的大小,利用redis key过期机制达到时间窗口目的,控制固定时间窗口内允许通过的请求量。

参考:
https://mdnice.com/writing/1989c852eec0486c8db3c1ee32d04d20

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/777396.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TCP首部格式【TCP原理(笔记五)】

文章目录 TCP首部格式源端口号&#xff08;Source Port&#xff09;目标端口号&#xff08;Destination Port&#xff09;序列号&#xff08;Sequence Number&#xff09;确认应答号&#xff08;Acknowledgement Number&#xff09;数据偏移&#xff08;Data Offset&#xff09…

Oracle 普通视图 (Oracle Standard Views)

视图&#xff08;views&#xff09;是一种基于表的"逻辑抽象"对象&#xff0c;由于它是从表衍生出来的&#xff0c;因此和表有许多相同点&#xff0c;我们可以和对待表一样对其进行查询/更新操作。但视图本身并不存储数据&#xff0c;也不分配存储空间。 本文只讨论普…

Linux下搭建pyqt5开发环境—基于Pycharm

防踩坑Tips&#xff1a; 1、不能学windows那样直接用pip安装PyQt5Designer和pyqt5-tools。这两个模块最根本的是用的windows的程序&#xff0c;linux上是运行不了的&#xff0c;特别是PyQt5Designer&#xff0c;会提示安装失败。 2、推荐在python环境安装同系统版本一致的pyq…

2023.7.16 第五十九次周报

目录 前言 文献阅读:跨多个时空尺度进行预测的时空 LSTM 模型 背景 本文思路 本文解决的问题 方法论 SPATIAL 自动机器学习模型 数据处理 模型性能 代码 用Python编写的LSTM多变量预测模型 总结 前言 This week, I studied an article that uses LSTM to solve p…

数据分析系统中的六边形战士——奥威BI系统

数据分析软件可以对收集的数据进行分析和报告&#xff0c;帮助企业获得更深入的数据洞察力&#xff0c;从而推动企业数字化运营决策&#xff0c;提高决策效率与质量。进入大数据时代&#xff0c;企业对数据分析软件的要求也在水涨船高&#xff0c;传统的数据分析软件显然已不能…

数据结构 单向链表(不循环)的基础知识和基础操作

头定义&#xff1a; typedef int datatype; typedef struct Node {//数据域存储数据datatype data;//指针域存储下一个地址struct Node *next; }*Linkelist; 创建节点 Linkelist create_node()//创建新节点 {Linkelist node(Linkelist)malloc(sizeof(struct Node));if(nodeN…

Elasticsearch 源码探究 001——故障探测和恢复机制

1、Elasticsearch 故障探测及熔断背景 探究Elasticsearch7.10.2 节点之间的故障探测以及熔断故障是怎么做的&#xff0c;思考生产上的最佳实践。 服务端故障场景&#xff1a; 单个master挂掉 除了断点断网&#xff0c;状态同步异常&#xff0c;主master也会认为自己已经失败&am…

ASPICE V模型之软件需求

ASPICE V模型之软件需求 了解ASPICE认识软件需求软件需求分解软件需求工作流程 了解ASPICE ASPICE全称是“Automotive Software Process Improvement and Capacity Determination”汽车软件过程改进及能力评定&#xff0c;是汽车行业用于评价软件开发团队的研发能力水平的模型框…

全球生成式AI大竞赛,Llama 2大模型现已可在亚马逊云科技上使用

一直以来Llama可以说是AI社区内最强大的开源大模型。但因为开源协议问题&#xff0c;一直不可免费商用。7月19日&#xff0c;Meta发布了大家期待已久的免费可商用版本Llama 2。一夜之间&#xff0c;大模型格局再次发生巨变。 作为Meta宣布的首批合作伙伴之一&#xff0c;现亚…

Ubuntu 安装Postgresql与PostGIS

1.前言 最近在做GIS分析&#xff0c;采集设备的经纬度点判断是否进出围栏以及是否产生道路偏移报警&#xff0c;在之前的文章有介绍过Windows下使用C#来实现&#xff0c;参考文章&#xff1a;利用PostgresqlPostgis进行空间地理信息分析&#xff08;道路偏移&#xff0c;进出电…

sql注入---报错注入

updatexml&#xff08;&#xff09;&#xff1a;对XML文档数据进行查询和修改 extractvalue&#xff08;&#xff09;&#xff1a;对XML文档数据进行查询 floor&#xff08;&#xff09;&#xff1a;取整的函数 前提是未关闭数据库报错函数&#xff0c;对于一些SQL语句的错误…

Java反射、动态代理

文章目录 反射什么是动态代理&#xff1f;程序为什么需要代理?代理长什么样? 反射 java反射机制原理&#xff1a;我们写的源代码是.java文件&#xff0c;通过javac编译后成为.class文件&#xff0c;即字节码文件&#xff0c;程序执行时&#xff0c;JVM会类加载字节码文件到内…

浅谈单片机

目录 1.什么是单片机 2.单片机的作用&#xff1f; 3.单片机的种类 4.如何学好单片机 5. 单片机的就业前景 1.什么是单片机 单片机&#xff08;Microcontroller Unit&#xff0c;简称MCU&#xff09;是一种集成了微处理器核心、存储器、输入输出端口和各种外围功能模块于一体…

Rspack 学习了解

一、简介 Rspack GitHub 仓库、Rspack Quick start。 Rspack 是由字节 ByteDance Web Infra 团队基于 Rust 语言开发的 Web 高性能构建工具。 Rust 是种高效、可靠的通用高级语言。其高效不仅限于开发效率&#xff0c;执行效率也是令人称赞的&#xff0c;属于少有兼顾开发效率…

vulnhub打靶--buli_b0x

目录 vulnhub--buli_b0x1.下载靶机2.导入靶机&#xff0c;开启靶机&#xff0c;nmap扫描3.探测目录&#xff0c;发现敏感目录4.通过test.php下载源码5.代码审计6.提权7.总结 vulnhub–buli_b0x 1.下载靶机 Billu_b0x.zip 2.导入靶机&#xff0c;开启靶机&#xff0c;nmap扫描…

分布式 RPC 框架HSF

分布式 RPC 框架HSF 概述HSF架构调用方式优势应用场景 概述 HSF (High-speed Service Framework)&#xff0c;高速服务框架&#xff0c;是在阿里巴巴内部广泛使用的分布式 RPC 服务框架。HSF 作为阿里巴巴的基础中间件&#xff0c;联通不同的业务系统&#xff0c;解耦系统间的…

WebSocket笔记

1. websocket介绍 WebSocket 是基于 TCP 的一种新的网络协议。它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成一次握手&#xff0c;两者之间就可以创建持久性的连接&#xff0c; 并进行双向数据传输。 HTTP协议和WebSocket协议对比&#xff1a; HTTP是短连接W…

iOS--动静态库

文章目录 认识动静态库静态库动态库静态的打包静态库的使用动态库的打包动态库的使用 动静态库的本质就是可执行程序的"半成品"。 需要完成一个可执行程序需要经历以下四个步骤: 预处理:完成头文件的展开&#xff0c;去掉注释&#xff0c;宏替换&#xff0c;条件编译…

微信小程序学习笔记(五)——优化

下拉刷新后主动关闭 Page({onPullDownRefresh: function() {// ...wx.stopPullDownRefresh()} })在发起请求时设置 loading&#xff0c;请求结束后关闭 Page({onLoad: function(options) {wx.showLoading({title: 数据加载中...}) // 展示 loadingwx.request({// ...complete: …

leetcode每日一练-第206题-反转链表

一、思路 迭代 二、解题方法 以输入为 1 -> 2 -> 3 -> 4 -> 5 的链表为例 三、code class Solution { public:ListNode* reverseList(ListNode* head) {ListNode* prevnullptr;//反转后的链表ListNode* currhead;//当前更新的原有链表while(curr)//原有链表无值…