apisix中limit-req漏桶限流解析

358 阅读2分钟

这个插件的原理, 是一个漏桶算法, 并且它是依赖redis实现的, 同时支持redisredis 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