阅读 309

业内限流常用技术方案 redis +lua sentinel guava

1:背景

在高并发业务场景下,常用的三板斧:"熔断、降级和限流"。接下来重点梳理一下常用的限流算法的几种实现方式。 相关测试代码见:https://gitee.com/javadev/data-x

 相关测试通过 ab -n 20 -c 15  http://localhost:8805/limiting/tpt
复制代码

2:常用解决方案

1:漏桶算法

漏桶算法思路很简单:我们把水比作是请求,漏桶比作是系统处理能力极限,水先进入到漏桶里,漏桶里的水按一定速率流出,当流出的速率小于流入的速率时,由于漏桶容量有限,后续进入的水直接溢出(拒绝请求),以此实现限流。

2:令牌桶算法

令牌桶算法的原理也比较简单,系统会维护一个令牌(token)桶,以一个恒定的速度往桶里放入令牌(token),这时如果有请求进来想要被处理,则需要先从桶里获取一个令牌(token),当桶里没有令牌(token)可取时,则该请求将被拒绝服务。令牌桶算法通过控制桶的容量、发放令牌的速率,来达到对请求的限制。

<dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>30.1.1-jre</version>
 </dependency>
 
static RateLimiter rateLimiter = RateLimiter.create(10);
if(!rateLimiter.tryAcquire()){
     String str = "guavalimiting 我被限流了啊 ,参数=" +name +"," + LocalDateTime.now();
     logger.error(str);
}
复制代码

3:redis& lua (滑动窗口限流)

Lua脚本和 MySQL数据库的存储过程比较相似,他们执行一组命令,所有命令的执行要么全部成功或者失败,以此达到原子性。也可以把Lua脚本理解为,一段具有业务逻辑的代码块。

-- 获取调用脚本时传入的第一个key值(用作限流的 key)

local c
c = redis.call('get',KEYS[1])

// 调用不超过最大值,则直接返回
if c and tonumber(c) > tonumber(ARGV[1]) then
return c;
end
// 执行计算器自加
c = redis.call('incr',KEYS[1])
if tonumber(c) == 1 then

// 从第一次调用开始限流,设置对应键值的过期
redis.call('expire',KEYS[1],ARGV[2])
end
return c;

KEYS[1] 用来表示在redis 中用作键值的参数占位,主要用來传递在redis 中用作keyz值的参数。

ARGV[1] 用来表示在redis 中用作参数的占位,主要用来传递在redis中用做 value值的参数。

复制代码

4:sentinel限流 (滑动窗口限流)

sentinel管控台结合@SentinelResource注解来实现服务限流以及熔断降级功能。

blockHandler只负责sentinel控制台配置违规,fallback只负责业务异常.自定义的限流降级方法参数必须带上BolckException,否则找不到定义的方法

blockHandlerClass限流降级处理类配置,单个fallback熔断降级处理方法配置。

image.png

sentinel 如果需要引入sentinel-dashboard,则需要通过引入spring-cloud-starter-alibaba-sentinel
复制代码

3: 总结

限流常在网关这一层做,比如Nginx、Openresty、Kong、Zuul、Spring Cloud Gateway等,而像spring cloud - gateway网关限流底层实现原理,就是基于Redis + Lua,通过内置Lua限流脚本的方式。

image.png

文章分类
后端
文章标签