API 报 429 怎么办?限流的正确处理姿势与错误做法
429 不代表你的代码有 bug,它是服务端在说「慢一点」。处理得好,用户几乎无感;处理得差,一次限流会滚雪球成整条链路的雪崩,而且每次失败的重试都在花钱。
这篇讲通用处理姿势。限流额度的两把尺子(RPM 与 TPM)怎么算,见 RPM 和 TPM 是什么。
先搞清楚是哪种 429
一、请求数超限(RPM)。 单位时间内发得太多。高频短请求的场景常撞这个。
二、token 吞吐超限(TPM)。 单位时间内处理的 token 太多。长文档、长上下文场景常撞这个,而且很隐蔽——你的请求数明明不多,却一直被拒。
三、并发数超限。 有些服务限制同时进行中的请求数,和速率是两个维度。
三者的应对方式不同:撞 RPM 要降请求频率,撞 TPM 要缩短请求长度或降频,撞并发要减少同时在飞的请求数。响应体里通常会说明是哪一类,先读它再动手。
响应里该读什么
多数厂商会在 429 响应里给出有用信息,别浪费:
- 建议等待时间(常见于响应头):有这个就按它等,别自己猜。
- 剩余额度与重置时间:可以用来做主动限速,而不是等被拒。
- 限流类型说明:区分 RPM / TPM / 并发。
- 请求 ID:提工单时要用。
把这些字段记进日志。没有日志的话,你只能看到「偶尔报 429」,看不到规律。
正确的处理姿势
第一,客户端主动限速,优先于事后重试。 在客户端用令牌桶把发出速率压在额度以内,从源头避免触发。这比撞墙后再退避优雅得多,也更省钱——被拒的请求不产生任何有效输出,但重试是要重新计费的。
第二,指数退避 + 随机抖动。 第一次等一小段,之后逐次翻倍,并加入随机量。抖动很关键:没有抖动的话,同时被拒的一批请求会在同一时刻集体重试,形成同步震荡,反复互相踩踏。
第三,设最大重试次数与总超时。 无限重试会让一次短暂限流演变成请求堆积,最终拖垮整个服务。超过上限就快速失败,把错误暴露出来。
第四,区分请求优先级。 用户正在等待的交互请求优先重试,后台批量任务直接降级或排队。所有请求一视同仁地抢额度,结果就是最重要的那些也被拖慢。
第五,把能挪的挪走。 离线任务走批处理接口(通常有独立额度),既省钱又把实时额度腾出来。见提示缓存和批处理该用哪个。
五种会让情况更糟的做法
一、立即重试,不等待。 服务端刚拒绝你,你马上又发一次,只会被再拒一次,还额外消耗额度判定。
二、固定间隔重试。 所有客户端节奏一致,形成周期性洪峰。
三、无限重试。 请求在内存里堆积,最终 OOM 或线程池耗尽,故障从「部分请求失败」升级成「服务不可用」。
四、把 429 当成致命错误直接抛给用户。 它本来是可恢复的,合理退避后大概率能成功。直接报错等于放弃了免费的恢复机会。
五、遇到 429 就无脑调大并发。 这是最反直觉但确实有人做的——以为是「不够快」,结果撞得更狠。方向反了。
长期解法:额度和结构一起看
短期靠退避扛住,长期要么提额、要么降需求。
提额通常需要付费历史或走商务流程,有周期,别等到撞墙才申请。
降需求的手段更多也更快见效:缩短输入直接降低 TPM 压力;合并小请求降低 RPM 压力;把不紧急的任务挪到批处理;给不同业务线分配独立密钥,避免一个批量任务把线上额度吃光。
最后这条特别实用:按业务隔离密钥。一个跑飞的离线脚本能把整个团队的线上服务拖垮,这种事故很常见,而隔离密钥就能挡住。
用户侧该怎么表现
技术上处理好了,还有一个产品问题:限流发生时用户看到什么。
最差的做法是直接抛出技术错误。 「HTTP 429 Too Many Requests」对用户毫无意义,只会带来工单。
较好的做法是分层降级:
- 能等的场景:显示「排队中,预计还需几秒」,后台安静重试。用户对有反馈的等待容忍度高得多。
- 不能等的场景:切换到备用模型或降级方案(规则处理、缓存结果、简化功能),保证核心流程不中断。
- 确实无法完成时:给一个明确的、可行动的提示(「当前服务繁忙,请稍后再试」),而不是技术堆栈。
流式场景要特别注意:如果限流发生在生成过程中,已经吐出去的内容要保留,别整段消失。处理方式见流式输出中断了怎么办。
一个容易忽略的成本视角
被限流拒绝的请求本身通常不计费(没有产生输出),但重试是要重新计费的。所以高 429 率的真实代价是:
- 重试带来的额外 token 消耗
- 用户等待时间变长带来的流失
- 排查和救火占用的工程时间
其中第一项是可以直接量化的:如果你的重试率是百分之十,那就是百分之十的成本花在了「同一件事做两遍」上。这个数字放进成本复盘里,通常比抽象的「限流问题」更容易推动资源投入。
监控做法见 API 成本监控,额度规划见大模型 API 的并发怎么规划。
监控里该有的两个指标
429 比例(429 次数 / 总请求数):超过阈值告警。它上升通常意味着流量涨了、并发配置改了、或者厂商额度政策变了。
重试后成功率:如果重试后大部分能成功,说明退避策略有效,只是额度紧张;如果重试后仍大量失败,说明额度严重不足,退避救不了,得去提额或降需求。
两个指标一起看,才能判断该调参数还是该动结构。埋点做法见 API 成本监控。
三个高频问题
问:429 会计费吗? 被拒绝的请求本身通常不产生 token 消耗,但重试成功的那次是要计费的。真正的成本在重试上。
问:调大并发能提高吞吐吗? 超过额度之后不能,只会增加被拒次数。要提吞吐得提额或降单请求大小。
问:偶发 429 需要处理吗? 需要。偶发意味着你已经贴着额度上限跑了,流量稍有波动就会大面积失败。提前留余量比事后救火便宜。
一句话记住
429 是流控信号而不是故障。正确的反应是放慢并等待,错误的反应是加速重试。系统层面的最优解是主动限速——在客户端就把速率压在额度以内,让 429 根本不发生。事后退避只是安全网,不该是主要手段。
接着往下看
限流处理属于「事后」,真正的解法在「事前」:额度怎么估、并发怎么配,见 RPM 和 TPM 是什么和大模型 API 的并发怎么规划。如果你的限流集中在长请求上,那问题多半出在输入长度而不是频率,见长文本任务的四个成本陷阱。