服务限流常见解决方案
一、背景
为了保证系统稳定性和可用性,我们有时候需要拒绝一些超出承受范围的请求;
服务限流就是其中的一种方案:限定请求的速率。
二、常见的限流算法
固定窗口计数器
原理
原理是将时间划分为固定大小的窗口,在每个窗口内限制请求的数量或速率,即固定窗口计数器算法规定了系统单位时间处理的请求数量。
假如我们规定系统中某个接口 1 分钟只能被访问 33 次的话,使用固定窗口计数器算法的实现思路如下:
- 将时间划分固定大小窗口,这里是 1 分钟一个窗口。
- 给定一个变量
counter
来记录当前接口处理的请求数量,初始值为 0(代表接口当前 1 分钟内还未处理请求)。 - 1 分钟之内每处理一个请求之后就将
counter+1
,当counter=33
之后(也就是说在这 1 分钟内接口已经被访问 33 次的话),后续的请求就会被全部拒绝。 - 等到 1 分钟结束后,将
counter
重置 0,重新开始计数。
特点
- 实现简单
- 限流不够平滑,可能出现两种情况
- 某个接口限流 一分钟30次,如果前30s有30次请求,那后30s无法处理请求
- 某个接口限流 一分钟30次,如果第1s内有30次请求,可能会超出接口处理能力。
滑动窗口计数器
原理
以上面窗口为60s为例,可以将窗口进一步划分成60 * 1s的小窗口,那么当前窗口就是由60个小窗口组成,
每过一个时间周期(1s)小窗口滑动一次,按照整个大窗口的请求数量来计算是否超出阈值。
特点
- 相比于固定窗口,滑动窗口可以应对激增流量
- 相比于固定窗口,可以提供更精确的限流控制
- 仍然出现限流不够平滑的问题(1)
漏桶
原理
类似于消息队列,服务以固定速率从队列中拿请求。
特点
- 实现简单
- 可以控制限流速率
- 无法应对激增流量
- 队列等待问题
令牌桶
原理
请求在被处理之前拿到令牌,请求完毕后将令牌丢弃。
按照一定速率向桶里添加令牌,如果装满了,就不能继续添加令牌。
特点
- 可以限制平均速率和应对激增流量
- 可以动态调整生成令牌的速率
三、单机限流
Guava RateLimiter
特点
基于令牌桶;开箱即用。
1 |
|
Demo
1 |
|
Resilience4j
特点
轻量级容错组件,提供限流、熔断、负载保护、自动重试等保障系统高可用的功能;
生态好,很多网关使用Resilience4j来做限流熔断;
绝大多数场景都适合;(Spring官方推荐,SpringCloud Gateway使用
1 |
|
四、分布式限流
常见方案:
- 借助中间件:借助Sentinel 或者使用Redis来实现
- 网关层限流:通常网关层也需要借助中间件/框架。
基于Redis的限流脚本,https://github.com/apache/shenyu
五、总结
限流是一种常见的系统保护方案(有损),通过拒绝掉部分请求保证大多数请求的正常响应。
常见的限流算法有四种
- 固定窗口计数器
- 滑动窗口计数器
- 漏桶算法
- 令牌桶算法
其中,比较常用的是滑动窗口计数器和令牌桶算法
单机场景下的限流,可以借助Guava组件或者Resilience4j来实现
分布式场景下,就需要借助中间件的帮助,常见方案是Redis+Lua脚本。
补充一句,我司常用的实现,是将集群限流按照机器数分摊到各个机器,最终只需要实现单机限流。
服务限流常见解决方案
https://yzaf.top/2024/rate-limiter/