跳到主要内容

API 限流速查:令牌桶到滑动窗口,限流算法与 429 响应头怎么看

讲清楚 API 限流的四种主流算法、429 状态码与 Retry-After 头怎么读,以及设计限流保护接口防滥用时,令牌桶、漏桶、固定窗口、滑动窗口各自适合什么场景。

发布于 作者 李雷
#api限流 #限流算法 #429 #retry-after #令牌桶

API 限流速查:令牌桶到滑动窗口,限流算法与 429 响应头怎么看

接口被刷爆,数据库被打满,第三方额度一夜耗光,这些事的解法常常都指向同一个词:限流。限流既是保护自己接口防滥用的护栏,也是你调用别人 API 时必须读懂的契约。这篇把四种主流限流算法、429 状态码,以及那几个容易看错的 HTTP 响应头讲清楚,配套工具是 API 限流速查,粘贴一段 429 响应头就能解释下一次安全重试时间。

限流到底在限什么

限流的本质是:在一个时间窗口里,只放行约定数量的请求,超出的部分被拒绝或排队。它保护的目标不止一个,既要防止恶意刷接口和爬虫拖垮服务,也要在多租户共享资源时给每个用户划一条公平线,还要让你的下游依赖不被突发流量冲垮。算法不同,放行节奏和对突发流量的容忍度就不同。

四种主流算法对比

固定窗口最直观:把时间切成整段,比如每分钟最多 100 次,计数到点清零。实现简单,但有个硬伤,窗口边界两侧可能挤进双倍流量,0 分 59 秒打 100 次,1 分 00 秒又打 100 次,两秒内 200 次。

滑动窗口是对它的修正。它不再到整点清零,而是按当前时刻往前看一整段时间,统计这段窗口内的真实请求数,所以流量曲线更平滑,不会出现边界处的尖峰。代价是要记录更细的时间信息,内存和计算开销略高。

令牌桶换了个思路:系统按固定速率往桶里放令牌,每个请求消耗一个令牌,桶有容量上限。桶里攒着令牌时,可以一次性放行一批,所以令牌桶允许突发流量,适合那种平时安静偶尔批量的场景。

漏桶正好相反,请求像水一样进桶,出口以恒定速率漏水,无论进来多猛,出去永远是匀速。它把突发彻底削平,适合需要严格保护下游恒定吞吐的链路,但对正常的短时突发也不留情面。

一句话记忆:固定窗口图省事,滑动窗口图平滑,令牌桶留余地给突发,漏桶要的是绝对匀速。

429 与那几个关键响应头

当你触发了别人的限流,标准答复是 HTTP 429 Too Many Requests,429 表示超限,而不是你请求本身有错。真正决定你下一步怎么做的,是随响应一起回来的头。

最该先看的是 Retry-After,它是服务端明确告诉你的等待时间,可能是秒数,也可能是一个 HTTP 日期。其次是 RateLimit-Limit、RateLimit-Remaining、RateLimit-Reset 这组标准头,分别表示额度上限、剩余额度、距离窗口刷新的时间。这里有个经典的坑:标准 RateLimit-Reset 通常是延迟秒数,而很多旧式 X-RateLimit-Reset 是 Unix 秒级时间戳,把后者当成要 sleep 的秒数,你就会让程序睡 17 亿秒。

举个真实例子。一个失败的同步任务返回了这样一段头:

HTTP/1.1 429 Too Many Requests
Retry-After: 30
RateLimit-Limit: 60
RateLimit-Remaining: 0
RateLimit-Reset: 30

读法是:这一分钟 60 次的额度已经用光,30 秒后窗口刷新,服务端建议你 30 秒后再来。正确做法是等满 30 秒,并叠加带抖动的指数退避,而不是立刻重试制造重试风暴。

我自己踩过的坑

我做过一个对接支付网关的 worker,超时后无脑重试,结果同一笔订单被创建了两次。后来才明白,非幂等的 POST 在重试前必须带 Idempotency-Key,服务端凭这个键去重,重试多少次都只生效一次。还有一次,我把 RateLimit-Remaining 当成了等待秒数,remaining 是剩余额度不是时间,等待时间永远只看 Retry-After 或 reset。这两个教训现在我都直接丢进速查表里随时翻。

自己设计限流接口时

如果你是接口的提供方,建议把限流维度和响应头一起想清楚:按用户、按 IP、按端点,还是按每分钟 token 数,每一种都该在响应头里如实标出来,别让调用方靠猜。返回 429 时务必带上 Retry-After,这是对客户端最大的善意。响应体若用 JSON 描述错误原因,记得保持结构规范,可以用 JSON 格式化工具 先校验一遍再交付文档。把算法选型、头部字段、重试策略集中放进一处,团队排障时就不必每次重新翻规范。


Made by Toolora · Updated 2026-06-13