Retry-After 是一个 HTTP 响应标头,告知应用在发送下一个请求之前等待多长时间。 该值为秒数或 HTTP 日期。 当 API 发送它时,它是“我何时可以重试?”的最可靠答案,因为它来自拒绝你请求的服务器。 有关详细信息,请参阅 RFC 9110,第 10.2.3 节。
Retry-After 的显示方式
标头有 2 种格式。 你的应用需要同时处理这两者。
| Format | Example | 含义 |
|---|---|---|
| Seconds | Retry-After: 120 |
收到响应后,请等待 120 秒(2 分钟)。 该值是非负整数。 |
| HTTP 日期 | Retry-After: Fri, 31 Dec 1999 23:59:59 GMT |
不要在此时间之前再次发送请求。 该日期始终以 GMT 表示。 |
服务器发送 Retry-After 时会附带以下状态代码:
| Status |
Retry-After是什么意思 |
Source |
|---|---|---|
429 Too Many Requests |
发送新请求之前等待多长时间。 服务器可以包含它。 | RFC 6585,第 4 节 |
503 Service Unavailable |
服务预计不可用的时间。 服务器可以包含它。 | RFC 9110 第 15.6.4 节 |
413 Content Too Large |
如果该情况是暂时性的,服务器应说明它将在多久后结束。 | RFC 9110 第 15.5.14 节 |
任何 3xx 重定向 |
跟踪重定向之前等待的最短时间。 | RFC 9110 第 10.2.3 节 |
标头是可选的。 某些 API 改用自己的标头。 例如,GitHub 会通过 x-ratelimit-reset 告诉你你的限制何时重置。 有关详细信息,请参阅超出 GitHub API 速率限制。
如何处理 Retry-After
- 读取两种格式。 如果值为数字,则为秒。 否则,将其分析为日期并减去当前时间。 如果日期已过去,可以立即重试。
- 至少等待标头所示的时间。 过早重试通常会让你再收到一个
429或503。 某些 API 在请求限制时会不断对请求进行计数,因此提前重试可能会延长等待时间。 例如,请参阅Microsoft Graph限制指南。 - 当缺少标头时,改用带抖动的退避策略。 在每个失败尝试后加倍等待,添加一个随机量,以便许多客户端不会在同一时刻重试,并限制等待。
- 限制重试次数。 尝试几次后,将错误返回到调用方。
- 检查重试是否有帮助。 当额度或支出限制用完时,某些 API 将返回
429。 等待无法修复这些问题。 有关示例,请参阅 OpenAI insufficient_quota和credit_balance_exhausted。
function retryDelayMs(response, attempt) {
const value = response.headers.get('retry-after');
if (value) {
const seconds = Number(value);
const ms = Number.isNaN(seconds) ? Date.parse(value) - Date.now() : seconds * 1000;
if (!Number.isNaN(ms)) {
return Math.max(ms, 0);
}
}
// No usable header: exponential backoff with jitter, capped at 30 seconds
return Math.random() * Math.min(30_000, 1_000 * 2 ** attempt);
}
热门 SDK 的作用
许多 SDK 为你处理 Retry-After,但只能处理到重试次数耗尽为止。 然后,代码将报错。
| SDK | 默认情况下,它的作用 |
|---|---|
| .NET标准复原处理程序 | 对 408、429 和 5xx 响应最多重试 3 次,并采用指数退避和抖动。 它使用 Retry-After 作为延迟值,因为 ShouldRetryAfterHeader 默认为 true。 |
| Microsoft Graph SDKs | 在存在时使用 Retry-After,在不存在时回退到指数退避。 不会自动重试 JSON 批处理中的请求。 |
| OpenAI Python SDK | 对连接错误以及 409、408、429 和 5xx 响应重试 2 次,并采用短暂的指数退避。 设置max_retries以更改它。 |
检查 SDK 的文档,了解确切的策略,并测试最后一次重试失败后会发生什么情况。
如何测试应用处理 Retry-After
你在开发过程中很少会遇到一个 Retry-After,而且即使遇到了,你也无法控制它的值。 因此,你如何测试这一点决定了你是否能在用户之前发现这些 bug。
| Approach | 你找到的内容 | 您错过的内容 |
|---|---|---|
| 等待生产 | 真实故障 | 所有情况,直到用户遇到它 |
| 在测试中模拟 API,或让编码代理编写模拟 | 你的代码是否解析该标头 | 真正的 HTTP 客户端或 SDK 是否等待了足够长的时间,以及 API 实际发送了什么内容。 你的应用还需要一个仅用于测试的开关来访问模拟。 |
| 调用真正的 API,直到它对你进行限流 | 实际表现 | 无法按需触发限流响应,并且会占用实际配额 |
| 截获应用的实际流量并按需返回限速响应 | 你的实际 SDK 和重试策略是否会等待标头所示的那么长时间 | 你的应用无需做任何更改,因此它不会对你的代码进行隔离测试。 将单元测试保留用于该目的。 |
在你的应用中试试
Dev Proxy 会拦截你的应用向所选 API 发出的请求,并返回带有 Retry-After 标头的 429 响应,而你的应用会继续调用真实的 URL。
RetryAfterPlugin 会记住何时可以重试每个受到限制的请求。 如果你的应用在该时间之前调用同一 URL,Dev Proxy 会报告此情况并再次对请求限流。 插件仅跟踪 429 响应。
在 GenericRandomErrorPlugin 的错误文件中,将 @dynamic 响应的 Retry-After 值设置为 429,而开发代理将填充秒数并跟踪它。
若要试用,请下载使用这两个插件的预设,并使用它启动开发代理:
devproxy config get openai-throttling
devproxy --config-file "~dataFolder/configs/openai-throttling/.devproxy/devproxyrc.json"
然后像往常一样运行应用并观察其运行情况。 若要安装 Dev Proxy,请参阅 设置 Dev Proxy。