这个插件的原理, 是一个漏桶算法, 并且它是依赖redis实现的, 同时支持redis和redis cluster, 对于kong而言社区版是不支持redis cluster的,需要商业版才行。
这是一段使用limit-req的配置:
rate: 10 # 限制为每秒5个请求
burst: 12 # 允许在1秒内突发3个请求
key: remote_addr # 根据客户端IP地址进行限制
那么使用这个配置的时候会进行一个转化, 将每个整数放大1000倍, 为什么呢, 稍后会看到。
function _M.new(plugin_name, conf, rate, burst)
local self = {
conf = conf,
plugin_name = plugin_name,
burst = burst * 1000,
rate = rate * 1000,
}
return setmetatable(self, mt)
end
在整个的实现中, 需要在redis中记录两个key, 一个是last_key, 记录上一个请求处理的时间, 另一个是excess_key,记录超出正常速率的token数。
key = "limit_req" .. ":" .. key
local excess_key = key .. "excess"
local last_key = key .. "last"
local excess, err = red:get(excess_key)
if err then
return nil, err
end
local last, err = red:get(last_key)
if err then
return nil, err
end
这里面有一个重要的公式,完美诠释其漏桶算法原理
if excess ~= ngx_null and last ~= ngx_null then
excess = tonumber(excess)
last = tonumber(last)
local elapsed = now - last
excess = max (excess - rate * abs (elapsed) / 1000 + 1000 , 0 )
if excess > self.burst then
return nil, "rejected"
end
else
excess = 0
end
**rate *** abs (elapsed) 计算中间逝去的这段时间 ,可以产生的token数。之所以放大1000倍,就是免去小数计算的问题。 excess初始为0, 而且如果中间这段时间足够长, 那么excess就会降为0。
journey
section 1rd request
0: 7
section elapsed
200ms: 7
section 2rd request
400ms: 7
section noe
600ms: 7
800ms: 7
1000ms: 7
举个例子, rate:1, burst:2, 如上图, 当第二个请求到来, **rate *** abs (elapsed) / 1000 = 400,
0 - 400 + 1000 = 600, 即超用了600, excess值将变为600. 最后延迟执行的时间, 将是 600/ 1000 = 0.6秒。
-- return the delay in seconds, as well as excess
return excess / rate, excess / 1000