一、常见的四种速率限流算法
对于限流,最为直接的就是速率限流了
- 固定窗口算法
- 比如 10r/s 就是把时间线分为 1s 一段,也就是周期为 1s,对一个时间段的请求进行计数,超过 10 则舍弃,未超过则直接处理
- 经过 1s 后,则进入新的周期
- 滑动窗口算法
- 比如 10r/s,但是现在把时间线分得更细了,比如 500ms 一段,但窗口还是 1s,依旧是窗口内的时间段的请求数超过 10 则舍弃,未超过则直接处理
- 经历 500ms 后,则进入新的周期,窗口也跟着滑过去了
时间线分得越细,滑动就越平滑
你可以理解为,固定窗口就是周期为 1s,窗口大小为 1s 的滑动窗口,但滑动是最不平滑的
窗口算法有个缺点,就是如果周期过大/滑动不平滑的话,可能会导致某两个周期临界的请求数很多
用滑动窗口确实也能一定程度的解决这个问题
- 漏桶算法 (在固定窗口算法的基础上实现)
在固定算法的基础上,允许突发流量的出现,若时间段内的请求数超了,超出的部分可以缓存到 burst 队列,在下一个周期处理,这样就保证请求漏出的速率是一致的,限流范围内的
如果桶满了就洒出去了~
如果 burst=0,那就跟固定窗口,没有区别
- 令牌桶算法 (无需计数)
通过一个固定的速率往令牌桶里输送令牌,而令牌桶有个最大值,达到最大就不能输送令牌了
请求需要有令牌才能被处理,如果没获得令牌,则直接拒绝
这个算法不是按照时间段计数的,所以没有窗口算法那样的临界问题,也允许了一定的突发量
二、速率限流
根据上线项目的 QPS 对比我们服务的负载能力,适当的对速率进行限流
(1)Nginx 实现固定窗口/漏桶算法
Nginx 的限流功能(如
limit_req
和limit_conn
)依赖于标准模块,通常情况下不需要额外安装模块。不过,如果你需要更高级的功能(如 Lua 脚本支持或分布式限流),可能需要安装额外的模块
突发请求量的处理:
- burst 就是漏桶的大小,burst 队列的大小
- 如果设置 nodelay,burst 队列里面的请求,会直接处理,而不是延迟
burst 里面的请求虽然会被立马处理,但在队列里的位置还是占着的,释放的速度还是按照漏出的速率来算,也就是说若出现以下场景:
- 第一秒来 15 个请求的话,【处理 10 个请求】,5 个放入 burst 队列并处理
- 第二秒来 15 个请求的话,【会优先释放 burst 里的 5 个请求,处理 5 个请求】,5 个放入 burst 队列并处理
- burst 没位置了,剩余的 5 个拒绝
- 第三秒来 10 个请求的话,【会优先释放 burst 里的 5 个请求,处理 5 个请求】,5 个放入 burst 队列并处理
- 第四秒来 10 个请求的话,【会优先释放 burst 里的 5 个请求,处理 5 个请求】,5 个放入 burst 队列并处理
- 第五秒来 7 个请求的话,【会优先释放 burst 里的 5 个请求,处理 5 个请求】,2 个放入 burst 队列并处理
- 第六秒来 7 个请求的话,【会优先释放 burst 里的 2 个请求,处理 7 个请求】
- 第七秒来 10 个请求的话,【处理 10 个请求】
(【…】代表正常速率 10r/s)
所以并不是说可以维持在 15r/s,而是某个瞬间可以达到,之后需要给请求慢下来就可以恢复(这样的效果其实类似于令牌桶算法,存在峰值快速处理与恢复期)
(2)滑动窗口算法
Nginx 需要安装 ngx_lua
模块,通过写 lua 脚本才能够实现滑动窗口限流算法,无论你熟不熟悉 lua 脚本,滑动窗口算法用 Nginx 来实现也是相当之麻烦的
滑动窗口算法可能需要从代码层面去解决,在企业里面在这方面应该会有基建
(3)网关实现令牌桶算法
三、限制连接数/并发量限流
思路:
- 连接越多或者时间越久,带来的请求就越多,这样就间接影响了速率,这是一个方面
- 另一个方面,多个连接存在,请求也会同时发送过来,需要考察服务器的并发处理能力,进行并发量限流也是必不可少的
(1)Tomcat 实现
redirectPort 是资源的访问需要 https,那需要重定向到服务的 https 端口
并不是拒绝后的重定向
(2)Nginx 实现
Nginx 的限流功能(如
limit_req
和limit_conn
)依赖于标准模块,通常情况下不需要额外安装模块。不过,如果你需要更高级的功能(如 Lua 脚本支持或分布式限流),可能需要安装额外的模块。
四、黑白名单
思路:一般正常用户的请求数是在限流范围内的,一些恶意的用户就可能比较危险,我们可以给出 IP 的黑白名单,对某个 IP 直接放行或者拒绝,整体来看,也是起到了限流的效果
# 屏蔽单个ip访问
deny IP;
# 允许单个ip访问
allow IP;
# 屏蔽所有ip访问
deny all;
# 允许所有ip访问
allow all;
#屏蔽整个段即从123.0.0.1到123.255.255.254访问的命令
deny 123.0.0.0/8
#屏蔽IP段即从123.45.0.1到123.45.255.254访问的命令
deny 124.45.0.0/16
#屏蔽IP段即从123.45.6.1到123.45.6.254访问的命令
deny 123.45.6.0/24
只允许某些 ip:
allow 192.168.100.1;
allow 192.168.100.2;
deny all;
只拉黑某些 ip:
deny 192.168.100.1;
deny 192.168.100.2;
可以将黑白名单配置写到一个配置文件,然后再引用这个配置文件,可以放到 http, server, location 语句块:
include blockip.conf;
五、其他
限制速率和限制连接数:
- 返回 503,代表服务器无法处理
黑白名单:
- 返回 403,代表客户端无权访问
分布式限流:
- 一般限流都可以通过外部中间件实现,让请求访问一个代理/网关(中心化),进行统一的限流,如果需要更定制和更严苛的限流,可能需要自己去写代码控制,使用到 redis、lua 脚本之类的
参考文章:
- 常用4种限流算法介绍及比较-CSDN博客
- 【SpringCloud】常见业务问题_网关限流和业务限流-CSDN博客
- Nginx 限流方式(速率限流、并发限流及黑白名单配置)_nginx 限流类型-CSDN博客