服务端与数据 / 进阶

用 Redis 做分布式限流:先定义你想限制什么

多实例服务不能依赖进程内计数器。本文讨论限流维度、Redis 原子操作、响应头和故障降级策略。

限流键比算法更重要

登录接口可以按 IP 加账号限流,普通业务 API 更适合按 userId 或 API key,昂贵的导出接口还要按租户限制并发。只按 IP 会误伤共享出口网络,只按账号则挡不住账号枚举。

匿名接口:可信代理解析后的 IP;认证接口:userId 或 API key,必要时叠加 IP;多租户接口:tenantId + route;高成本任务:速率限制之外再加并发上限。

计数与过期必须原子执行

固定窗口实现简单,但窗口边界可能出现突发流量。需要更平滑的速率时选择滑动窗口或令牌桶。无论算法如何,检查、扣减和设置过期都要作为一个原子操作。

EXAMPLE / 02Redis
local current = redis.call('INCR', KEYS[1])
if current == 1 then
  redis.call('PEXPIRE', KEYS[1], ARGV[1])
end
return { current, redis.call('PTTL', KEYS[1]) }

告诉客户端何时可以重试

超过限制时返回 429,并带 Retry-After。也可以返回 RateLimit-Limit、RateLimit-Remaining 和 RateLimit-Reset,便于 SDK 做退避。Redis 不可用时是放行还是拒绝,要按接口风险决定:内容读取可短暂放行,登录和短信接口更适合保守失败。

只有请求确实经过受控反向代理时才能信任转发头,并配置正确的代理跳数,否则攻击者可以伪造 IP 绕过限流。