QPS令牌桶算法实现
功能说明:
- 实现基于令牌桶的QPS限流
- 支持突发流量处理(burst)
- 自动补充令牌
参数说明:
- KEYS[1]: 令牌桶key
- ARGV[1]: token_interval_ms - 生产一个令牌需要的毫秒数
- ARGV[2]: burst_limit - 令牌桶最大容量(突发上限)
- ARGV[3]: required_tokens - 需要获取的令牌数目
返回值:
- {acquired_tokens, remaining_tokens}
- acquired_tokens: 实际获取的令牌数(0表示获取失败)
- remaining_tokens: 获取后剩余的令牌数
]]
redis.replicate_commands()
local function getCurrentMillis()
local current = redis.call('TIME')
return current[1] * 1000 + math.modf(current[2] / 1000)
end
local key = KEYS[1]
local token_interval_ms = tonumber(ARGV[1])
local burst_limit = tonumber(ARGV[2])
local required_tokens = tonumber(ARGV[3])
local current_time_ms = getCurrentMillis()
local TOKENS_FIELD = "tokens"
local LAST_REFILL_FIELD = "last_refill"
local state = redis.call('HMGET', key, TOKENS_FIELD, LAST_REFILL_FIELD)
local current_tokens, last_refill
if not state[1] then
current_tokens = burst_limit
last_refill = current_time_ms
else
current_tokens = tonumber(state[1]) or burst_limit
last_refill = tonumber(state[2]) or current_time_ms
end
local elapsed_ms = current_time_ms - last_refill
local new_tokens = elapsed_ms / token_interval_ms
local updated_tokens = math.min(burst_limit, current_tokens + new_tokens)
local acquired_tokens = math.min(required_tokens, updated_tokens)
if acquired_tokens < 1 then
return {0, updated_tokens}
end
updated_tokens = updated_tokens - acquired_tokens
local expire = math.ceil(burst_limit * token_interval_ms / 1000) + 1
expire = math.max(expire, 60)
redis.call('HMSET', key, TOKENS_FIELD, updated_tokens, LAST_REFILL_FIELD, current_time_ms)
redis.call('EXPIRE', key, expire)
return {acquired_tokens, updated_tokens}