四种算法对比
| 算法 | 特点 | 边界问题 | 适用 |
|---|---|---|---|
| 固定窗口 | 按整段时间计数 | 窗口交界出现双倍突发 | 简单统计 |
| 滑动窗口 | 近似平滑 | 实现稍复杂 | 更均匀的限流 |
| 令牌桶 | 允许一定突发 | 需维护桶容量 | 面向用户的 API |
| 漏桶 | 恒定速率流出 | 突发会被排队或丢弃 | 保护下游、削峰 |
分布式计数
多实例下要用共享存储:Redis 的 INCR + EXPIRE 或 Lua 脚本保证原子;否则每台机器各算各的,等于没限。
三个关键做法
- 返回 429 + Retry-After:让客户端知道何时重试,而不是盲目重试;
- 按身份维度限流:用户、API Key、租户分别计数,别只按 IP(NAT 下会误伤一群人);
- 配合退避抖动:客户端重试加指数退避和随机抖动,避免重试风暴把服务打挂。
和幂等、重试的关系
限流解决「别打太猛」,幂等解决「重试会不会重复生效」。两者配合,重试才安全。相关:API 幂等、重试与超时。
与业务策略的配合
限流不是孤立的中间件配置,它需要与业务规则配合才能真正有效。
- 分级而非一刀切:为不同接口设定不同阈值,登录与短信接口最严,查询类接口可宽松。用同一套阈值覆盖所有接口,要么误伤正常业务,要么对昂贵接口保护不足。
- 区分正常突发与异常扫描:允许用户短时间内连续操作的正常模式,同时对均匀高频、遍历式访问提高警惕。仅按次数限流会把正常用户和爬虫一起拦下。
- 给调用方明确反馈:除了返回状态码与重试时间,还应在响应头中给出剩余额度与重置时间,让客户端能自我调整,而不是靠反复试错。
- 限流要能临时调整:大促、活动或故障期间需要动态放宽或收紧。把阈值做成可热更新的配置,比改代码重新发布快得多。
- 防止绕过:同一用户的多个来源地址、多设备登录都应能被聚合到同一维度,否则攻击者只需切换地址即可绕过。
上线后的校验
限流上线后应验证三件事:正常用户的误伤率是否可接受、被限流的请求是否确实异常、以及限流本身是否引入了额外延迟。缺少校验的限流往往在真正发生攻击时才发现阈值形同虚设。
与计费、配额的衔接
限流之外,往往还需要配额管理:前者防止瞬时过载,后者控制总量并支撑计费。两者应基于同一套计量口径,否则会出现“限流没触发但配额已耗尽”这类难以向用户解释的情况。计量数据的采集精度与延迟也应与用户可见的用量保持一致。