Redis 做接口限流:固定窗口、滑动窗口和令牌桶怎么选

0 次浏览0 条回复

登录、发验证码、开放 API 都需要限流。用 Redis 实现时,常见的有三种算法。

固定窗口

每个时间窗口一个计数器,超过上限就拒绝。实现最简单,缺点是窗口交界处可能在短时间内放过两倍的请求。

Lua
-- KEYS[1]: 计数键  ARGV[1]: 窗口秒数  ARGV[2]: 上限
local current = redis.call("INCR", KEYS[1])
if current == 1 then
  redis.call("EXPIRE", KEYS[1], ARGV[1])
end
if current > tonumber(ARGV[2]) then
  return 0
end
return 1

INCR 和 EXPIRE 要放在同一个 Lua 脚本里原子执行。分两条命令发的话,进程恰好在中间崩溃,就会留下一个永不过期的键,这个用户会被一直拦住。

滑动窗口

用有序集合记录每次请求的时间戳,先 ZREMRANGEBYSCORE 删掉窗口外的记录,再用 ZCARD 计数。结果精确,但每个请求占一条记录,流量大时内存开销明显。

令牌桶

按固定速率往桶里放令牌,每个请求取走一个,桶空了就拒绝。既允许一定的突发,又能限制平均速率,适合对外开放的 API。

怎么选

  • 登录、验证码这类防刷场景:固定窗口足够。
  • 要求“任意 60 秒内不超过 N 次”:滑动窗口。
  • 按用户或应用配额的开放接口:令牌桶。

另外两点:限流的键要带上维度(用户 ID 或 IP 加接口名);被限流时返回 HTTP 429,并带上 Retry-After 头告诉客户端多久后再试。

0 条回复

返回板块

还没有回复

欢迎发表你的看法。

发表回复