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

1:背景
在高并发业务场景下,常用的三板斧:"熔断、降级和限流"。接下来重点梳理一下常用的限流算法的几种实现方式。

相关测试代码见:https://gitee.com/javadev/data-x
【springboot|业内限流常用技术方案 redis +lua sentinel guava】相关测试通过 ab -n 20 -c 15 http://localhost:8805/limiting/tpt
2:常用解决方案
1:漏桶算法

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

2:令牌桶算法

令牌桶算法的原理也比较简单,系统会维护一个令牌(token)桶,以一个恒定的速度往桶里放入令牌(token),这时如果有请求进来想要被处理,则需要先从桶里获取一个令牌(token),当桶里没有令牌(token)可取时,则该请求将被拒绝服务。令牌桶算法通过控制桶的容量、发放令牌的速率,来达到对请求的限制。
com.google.guava guava 30.1.1-jre 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熔断降级处理方法配置。
字段名称
字段解释
resource
资源名,即限流规则的作用对象
limitApp
流控针对的调用来源,若为 default 则不区分调用来源
grade
限流阈值类型(QPS 或并发线程数);0代表根据并发数量来限流,1代表根据QPS来进行流量控制
count
限流阈值
strategy
调用关系限流策略
sentinel 如果需要引入sentinel-dashboard,则需要通过引入spring-cloud-starter-alibaba-sentinel

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


    推荐阅读