最近收到个给接口限制QPS的需求,起初不知道从哪入手,折腾了几天,终于找到了些方案,在此记录一下吧。
限速常见算法
漏桶算法(leaky bucket)
算法原理大概如图
大致可理解为
- 水(请求)从上方倒入水桶,从水桶下方流出(被处理);
- 来不及流出的水存在水桶中(缓冲),以固定速率流出;
- 水桶满后水溢出(丢弃)。
这里我个人理解为:
强调的是匀速处理请求,毕竟水是匀速流出的。
令牌桶算法(token bucket)
算法原理大概如图
基本思想:
- 令牌以固定速率产生,并缓存到令牌桶中;
- 令牌桶放满时,多余的令牌被丢弃;
- 请求要消耗等比例的令牌才能被处理;
- 令牌不够时,请求被缓存。
然而令牌桶算法中,最大不同是它多了这个桶,它可以把令牌(可以理解为通行证)缓存起来,也就是说在某个时间段内,它可以不均匀的消耗令牌。
举个栗子:某个接口允许每秒100的QPS。
漏桶算法:平均到毫秒的话,大概就是每10ms允许通过一个请求,它强调匀速。
令牌桶算法:它则可以在前10ms通过全部的100个请求,剩余的时间不让通过(实际有些不同,毕竟受限于令牌桶容量以及它令牌是匀速产生的)。
nginx限速
下面使用的是 lua-resty-limit-traffic,该模块主要用于在OpenResty/ngx_lua中控制流量。
其中QPS限制主要有两种模式,即resty.limit.req
和resty.limit.count
。
相关环境为Windwos上使用openresty。
resty.limit.req
这个是类似前面漏桶算法的实现。使用方法如下:
nginx.conf
文件部分内容
location /other {
access_by_lua_block {
-- https://github.com/openresty/lua-resty-limit-traffic
-- well, we could put the require() and new() calls in our own Lua
-- modules to save overhead. here we put them below just for
-- convenience.
local limit_req = require "resty.limit.req"
-- limit the requests under 200 req/sec with a burst of 100 req/sec,
-- that is, we delay requests under 300 req/sec and above 200
-- req/sec, and reject any requests exceeding 300 req/sec.
-- 关键是这两个参数,正常即200QPS,瞬间的话允许300QPS