Retry-After 是 HTTP 回應標頭,告訴你的應用程式在傳送下一個要求之前應等待多久。 這個數值可以是秒數或 HTTP 日期。 當 API 傳送它時,它是對「什麼時候可以再試?」這個問題最可靠的答案,因為它來自拒絕你請求的伺服器。 欲了解更多資訊,請參閱 RFC 9110,第 10.2.3 節。
Retry-After 的樣貌
標頭有兩種格式。 你的應用程式需要同時處理這兩者。
| 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 | 重試連接錯誤與 408、 409、 429及 5xx 回應兩次,並以短指數退回。 設定 max_retries 要更改它。 |
請查看你的 SDK 文件,確認具體的政策內容,並測試最後一次重試失敗後會發生什麼。
如何測試你的應用程式是否能處理 Retry-After
你在開發時很少會有 Retry-After,即使有,也無法控制它的價值。 所以你測試的方式決定了你是否比使用者先發現錯誤。
| Approach | 你發現的 | 你缺少的內容 |
|---|---|---|
| 等待正式環境 | 實際失敗 | 在使用者按下前的一切 |
| 在測試中模擬 API,或讓你的編碼代理寫出模擬 | 你的程式碼是否解析標頭 | 你的真實 HTTP 客戶端或 SDK 是否等待足夠久,以及 API 真正傳送的內容。 你的應用程式也需要一個僅供測試使用的開關來連線到 mock。 |
| 呼叫實際 API,直到 API 對你進行速率限制 | 實際行為 | 你無法按需觸發限速回應,且會用掉真正的配額 |
| 攔截應用程式的真實流量,並視需要傳回節流回應 | 不管是你的真實 SDK 和重試政策,都要等標頭上寫的那段時間 | 你的應用程式本身沒有任何變動,所以它不會單獨測試你的程式碼。 把那個留給單元測試。 |
在你的應用程式中試試看
Dev Proxy 會攔截你應用程式對你選擇的 API 的請求,並回傳 429 帶有 Retry-After 標頭的回應,而你的應用程式則持續呼叫真實的 URL。
RetryAfterPlugin 會記住每個遭到節流的請求可於何時重試。 如果你的應用程式在那之前呼叫同一個 URL,Dev Proxy 會回報該情況,並再次限制請求。 外掛程式僅追蹤 429 的回應。
在 GenericRandomErrorPlugin 的錯誤檔案中,將 429 回應的 Retry-After 值設為 @dynamic,開發代理會自動填入秒數並幫你追蹤。
若要試用,請下載一個同時使用這兩個外掛程式的預設集,然後用它啟動 Dev Proxy:
devproxy config get openai-throttling
devproxy --config-file "~dataFolder/configs/openai-throttling/.devproxy/devproxyrc.json"
然後照常執行你的應用程式,觀察它的表現。 要安裝 Dev Proxy,請參見 設定 Dev Proxy。