3

你们是怎么实现 rate limiting 的?

 2 years ago
source link: https://www.v2ex.com/t/839542
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

V2EX  ›  程序员

你们是怎么实现 rate limiting 的?

  JasonLaw · 17 小时 44 分钟前 · 953 次点击

我所实现的 rate limiting 是基于How to implement rate limiting using Redis - Stack Overflow的,rateLimiting.lua如下:

-- https://stackoverflow.com/questions/13175050/how-to-implement-rate-limiting-using-redis
-- KEYS[1] = rateLimitingKey, ARGV[1] = timePeriodInSeconds, ARGV[2] = allowableNumberOfCalls, ARGV[3] = nowEpochSecond
local timePeriodInSeconds = tonumber(ARGV[1])
local allowableNumberOfCalls = tonumber(ARGV[2])
local nowEpochSecond = tonumber(ARGV[3])
local times = redis.call('RPUSH', KEYS[1], nowEpochSecond)
if times > allowableNumberOfCalls then
    local timeStart = redis.call('LINDEX', KEYS[1], 0)
    local timeEnd = redis.call('LINDEX', KEYS[1], -1)
    redis.call('LTRIM', KEYS[1], -allowableNumberOfCalls, -1)
    if timeEnd - timeStart <= timePeriodInSeconds then
        return false
    else
        return true
    end
else
    return true
end

如果 times <= allowableNumberOfCalls ,直接返回 true ,如果 times > allowableNumberOfCalls ,那么判断一下 timeEnd - timeStart <= timePeriodInSeconds ,如果结果为 true ,那么 return false ,否则 return true 。

但是这有个问题,因为 nowEpochSecond 是服务实例告诉 Redis 的,但多个实例的时钟是无法确保一致的,那么会出现如下情况。

假设 timePeriodInSeconds 为 5 ,allowableNumberOfCalls 为 1 ,当前时间(正确的时钟)为 2022-03-10 22:00:00 ,实例 1 的时钟跟正确的时钟一样,实例 2 的时钟比正确的时钟慢了 3 秒。

用户此时访问了资源,该请求被实例 1 处理,能获取到资源,因为 ta 之前从没访问过,所以不会被限制。过了 6 秒,ta 又访问了资源,正常的结果是用户可以访问到资源,但是结果并不是,因为这次请求被实例 2 处理,而实例 2 此时的时间为 2022-03-10 22:00:03 ,rateLimiting.lua 最后会返回 false 。

怎么解决上述问题?或者有什么更好的方法实现 rate limiting ?


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK