Appearance
计数器与限流
实现方案
1. 固定窗口
lua
-- 固定1分钟内100次
local key = "rate:limit:" .. user_id .. ":" .. os.time() // 60
local count = redis.incr(key)
if count == 1 then
redis.expire(key, 60)
end
return count <= 1002. 滑动窗口
lua
-- 滑动窗口去重
local key = "rate:window:" .. user_id
local now = os.time()
redis.zadd(key, now, now .. ":" .. math.random())
redis.zremrangebyscore(key, 0, now - 60) -- 移除60秒前的
local count = redis.zcard(key)
return count <= 1003. 令牌桶
lua
local key = "rate:bucket:" .. user_id
local last = tonumber(redis.hget(key, "last") or 0)
local tokens = tonumber(redis.hget(key, "tokens") or 10)
local now = os.time()
local delta = (now - last) * 1.0 -- 每秒1个令牌
tokens = math.min(10, tokens + delta)
if tokens >= 1 then
tokens = tokens - 1
redis.hset(key, "last", now)
redis.hset(key, "tokens", tokens)
return true
else
return false
end分布式限流
简单实现
lua
-- 多Redis实例汇总
local count = 0
for _, ip in ipairs(redis_ips) do
count = count + redis.call("get", "rate:" .. ip)
end
return count <= limit面试考点
Q: 固定窗口vs滑动窗口?
- 固定窗口:简单,可能有临界突刺
- 滑动窗口:精确,无突刺
Q: 令牌桶适合什么场景?
适合需要平滑限流的场景,允许一定程度的突发流量。
