rate-limit-headers · ZH · 2026-10-13

读懂聚合站返回的限流响应头:哪几个字段真正决定你下一次能不能发

当请求被限流时,响应头比错误消息更有用。本文逐个解释聚合站在代理层可能透出的限流相关响应头,区分哪些字段反映聚合站自身的配额与队列,哪些来自上游厂商,并给出记录日志、做退避决策的实用方法。

为什么限流时要看响应头

被限流时,响应体往往只有一句 rate limit exceeded,真正有用的信息常常藏在响应头里。聚合站在你的请求和上游厂商之间做转发,因此返回的头可能来自两个层面:聚合站自身的配额与排队策略,以及上游厂商透传的限制信号。

学会区分它们,你才能判断该等多久、该换 key,还是该改请求节奏。

常见的限流相关响应头

不同厂商的命名不完全一致,但大体围绕下面几类。聚合站通常会保留原始头,也可能追加自己的前缀。

  • 剩余额度类:如 x-ratelimit-remaining-requests、x-ratelimit-remaining-tokens。表示当前窗口内还剩多少请求次数或 token。数值通常按窗口滑动。
  • 重置时间类:如 x-ratelimit-reset-requests、x-ratelimit-reset-tokens。表示剩余额度恢复的大致时间,可能是秒数,也可能是 1s、6m0s 这样的时长字符串。
  • 重试建议类:retry-after。出现 429 或 503 时常见,直接告诉客户端建议等待多久再试。这个字段优先级最高,应当优先遵守。
  • 限制总量类:如 x-ratelimit-limit-requests、x-ratelimit-limit-tokens。表示你在当前维度上的上限,用来计算使用比例。
  • 请求 ID 类:如 x-request-id、request-id。它不直接决定能不能发,但排查和申诉时必带。

哪些来自聚合站,哪些来自上游

同一个请求穿过聚合站再打到上游,头可能被叠加。区分方法是看语义和命名。

  • 偏聚合站的字段:与你的账户余额、站内并发、key 分组、队列位置相关的字段。常见做法是加自定义前缀,如 x-proxy-、x-gateway-,或直接在错误体里给出站内配额说明。
  • 偏上游的字段:标准的 x-ratelimit- 系列、retry-after、部分厂商特有的 anthropic-ratelimit- 一类命名。这些反映的是上游对聚合站账号的限制,不一定等于聚合站分给你的额度。

需要提醒的是:上游的剩余额度不等于你能用的额度。聚合站可能对多个用户共享的上游账号做了池化,也可能对每个用户单独限速。因此看到上游头显示还有余量,不代表你立刻就能发;反过来,上游头归零也不一定说明你被拒。

收到 429 时的判断顺序

可以按下面的顺序做决策:

  1. 先看有没有 retry-after。有就按它等待,不要立即重试。
  2. 再看站内自定义头。如果聚合站给出了队列长度或站内配额,说明瓶颈在聚合层,重试可能无意义,考虑换 key、换模型或降低并发。
  3. 最后看上游的 x-ratelimit-*。如果剩余请求数很低而重置时间很近,短暂等待即可;如果重置时间很远,说明需要调整调用节奏。

避免固定间隔的忙重试。固定重试会在限流窗口内持续堆积请求,反而延长恢复时间。

把这些字段记进日志

只靠肉眼排查不够,建议在客户端统一记录。

  • 记录状态码、x-request-id、全部 x-ratelimit-*、retry-after,以及聚合站自定义的限流头。
  • 记录收到响应的时间戳和本次请求的模型名、key 标识(脱敏后)。
  • 日志中把「站内头」和「上游头」分开存放,方便后续统计哪一层先被打满。
  • 如果做重试,同时记录重试次数和第几次成功,便于估算真实可用速率。

有了这些数据,你可以画出随时间变化的剩余额度曲线,判断是偶发突发还是持续超限。

一个简单的退避策略

响应头是可以直接驱动退避的:

  • 有 retry-after:按值等待,可加小抖动避免同时重试。
  • 只有重置时间:等待到重置点附近再加抖动。
  • 什么都没透出:采用指数退避加抖动的默认策略,并限制最大重试次数。

在客户端批量并发时,建议对同一 key 做全局的节流,而不是每个请求各自退避,否则多个请求同时等待会放大冲击。

小结

限流响应头里,retry-after 决定「等多久」,x-ratelimit-remaining-* 和 reset 类字段决定「还能不能发」,自定义前缀字段则告诉你瓶颈在聚合站还是上游。把它们完整记进日志,并据此做带抖动的退避,比盲目重试更省事,也更容易定位问题。