Retry-After 標題:要等多久才會重試

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

  1. 讀取兩種格式。 如果該值是數字,則以秒為單位。 否則,將其解析為日期,然後減去目前的時間。 如果日期已過,你可以立即重試。
  2. 至少等待與標頭所示一樣長的時間。 早點重試通常只會再次出現 429 或 503。 有些 API 在對你的請求進行限流時會持續計算你的請求,所以提前重試會讓等待時間變長。 例如,請參閱 Microsoft Graph 限速指引。
  3. 當標頭缺失時,回退為使用帶抖動的退避機制。 每次失敗後將等待時間加倍,隨機增加等待時間,避免多個客戶同時重試,並限制等待時間。
  4. 限制重試次數。 嘗試幾次後,將錯誤訊息回傳給呼叫端。
  5. 確認重新嘗試是否有幫助。 有些 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 會幫你處理 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。

下一步

也請參閱