当前位置:网站首页>分布式限流 redission RRateLimiter 的使用及原理
分布式限流 redission RRateLimiter 的使用及原理
2022-07-28 22:25:00 【肥肥技术宅】
前提
最近公司在做有需求在做分布式限流,调研的限流框架大概有
1、spring cloud gateway集成redis限流,但属于网关层限流
2、阿里Sentinel,功能强大、带监控平台
3、srping cloud hystrix,属于接口层限流,提供线程池与信号量两种方式
4、其他:redission、手撸代码
实际需求情况属于业务端限流,redission更加方便,使用更加灵活,下面介绍下redission分布式限流如何使用及原理:
一、使用
使用很简单、如下
// 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.ALL与RateType.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过期机制达到时间窗口目的,控制固定时间窗口内允许通过的请求量。

边栏推荐
- [TA frost wolf \u may - "hundred people plan"] Figure 3.6 texture compression - inclusion slimming
- Detailed principle explanation and verification results of digital clock based on FPGA
- Intelligent trash can (VII) -- Introduction and use of sg90 steering gear (Pico implementation of raspberry pie)
- 【微服务】Nacos集群搭建以及加载文件配置
- Software designer afternoon question
- Have passed hcip and joined the company of your choice, and share the learning experience and experience of Huawei certification
- Sword finger offer 41. median in data flow
- Detailed explanation of the usage of exists in MySQL
- Attack and defense world web master advanced area php2
- What does WGet mean
猜你喜欢

vulnhub:Sar

Eye of depth (18) -- partial derivative

How can Plato obtain premium income through elephant swap in a bear market?

vulnhub:BTRSys2

Compilation principle research study topic 2 -- recursive descent syntax analysis design principle and Implementation

熊市下PLATO如何通过Elephant Swap,获得溢价收益?

Advanced area of attack and defense world web masters training www robots

With this, your messages can't be monitored

递归/回溯刷题(下)

MySql中的like和in走不走索引
随机推荐
2022网络安全学习路线 非常详细 推荐学习
Cmake basic learning
Advanced area of attack and defense world web masters unserialize3
Leetcode64. Minimum path sum
CV instance segmentation model sketch (1)
动态规划问题(三)
熊市下PLATO如何通过Elephant Swap,获得溢价收益?
Real time data warehouse: meituan reviews Flink's real-time data warehouse application sharing
Install MySQL using Yum for Linux
Principle of meter skipping
IDEA报错Error running ‘Application‘ Command line is too long解决方案
Detailed principle explanation and verification results of digital clock based on FPGA
Have passed hcip and joined the company of your choice, and share the learning experience and experience of Huawei certification
1-8 basic use of props
Oracle create tablespaces and users
Type 1-5 components
@Transactional 注解使用详解
Interpretation of ISO 13400 (doip) standard
The difference between {} and ${}
跳表的原理