GitHub API 已超過速率限制:這代表什麼以及如何處理

GitHub 限制了你的應用程式能發送的 REST API 請求數量。 當你超過限制時,GitHub 會以 429 或 403 狀態拒絕你的請求,直到限制重置或等待時間結束。 GitHub 有兩種限制:主要限制每小時請求數,以及防止突發的次級限制。 如果你的應用程式在速率限制期間持續發送請求,GitHub 可能會禁止你的整合。 欲了解更多資訊,請參閱 REST API 的速率限制。

GitHub 的速率限制長什麼樣子

Limit 價值 當你檢視它時
主要、未經驗證 每個 IP 位址每小時 60 次請求 403 或 429,且 x-ratelimit-remaining 是 0
主要個人存取權杖 每小時5,000個請求 同上
主要項目,GITHUB_TOKEN在 GitHub Actions 中 每個儲存庫每小時 1,000 筆請求 同上
次要 例如,同時請求不超過 100 筆,REST 端點每分鐘 900 點數,約每分鐘 80 筆內容產生請求 403 或 429,並顯示錯誤訊息。 retry-after 可能存在。

GitHub 可以在不事先通知的情況下更改次要限制,而且也無法查看你離這些限制還有多近。

每個回應都會有標頭告訴你你在主要限制上的進度:

Header 它告訴你的
x-ratelimit-limit 每小時最多可傳送的請求數
x-ratelimit-remaining 你目前還剩多少請求
x-ratelimit-used 你在目前時間區間內已傳送多少個請求
x-ratelimit-reset 當視窗重置時,以 UTC Unix 紀元秒數計算
x-ratelimit-resource 請求會計入哪個限制

如何處理 GitHub 的速率限制

  1. 分辨速率限制與權限錯誤。 當你的令牌缺乏存取權時,GitHub 也會回傳403。 如果回應沒有 retry-after、x-ratelimit-remaining 不是 0,且訊息中沒有提到速率限制,那就是權限問題。 不要重試它。
  2. 先追蹤 retry-after。 如果標頭存在,就等那麼多秒。
  3. 否則,就等待重設完成。 如果 x-ratelimit-remaining 是 0,請不要重試,直到 x-ratelimit-reset 顯示的時間。
  4. 否則,至少等一分鐘。 如果是沒有任何標頭的次要限制,GitHub 會要求你至少等待 1 分鐘,且每次重試失敗後再等更久。 在設定次數的重試後停止並引發錯誤。
  5. 在用完之前放慢速度。 使用x-ratelimit-remaining和x-ratelimit-reset調整你的請求節奏。 不要用精確的剩餘計數來建立邏輯,因為 GitHub 可以改變限制。 x-ratelimit-*標頭才是權威來源,不是 GET /rate_limit 端點。
async function githubWaitMs(response, attempt) {
  if (response.status !== 403 && response.status !== 429) {
    return null;
  }
  const retryAfter = response.headers.get('retry-after');
  if (retryAfter) {
    return Number(retryAfter) * 1000;
  }
  if (response.headers.get('x-ratelimit-remaining') === '0') {
    const resetMs = Number(response.headers.get('x-ratelimit-reset')) * 1000;
    return Math.max(resetMs - Date.now(), 0);
  }
  const { message = '' } = await response.clone().json().catch(() => ({}));
  if (response.status === 429 || /rate limit/i.test(message)) {
    return 60_000 * 2 ** attempt;
  }
  // A 403 without rate limit signals is a permission problem: don't retry
  return null;
}

當函式回傳一個數字時,呼叫者會重試,並在嘗試幾次後停止。

如何測試你的應用程式是否能處理 GitHub 的速率限制

開發時很少會遇到 GitHub 的速率上限。 你只要發送幾個請求,每小時 5,000 個就感覺像是用不完。 所以你測試速率限制處理的方式,決定了你是否比使用者先發現錯誤。

Approach 你發現的 你錯過什麼
等待正式環境 實際失敗 所有內容,直到使用者點選它
在測試中模擬 API,或讓你的程式代理寫出模擬 你的重試分支是否執行 GitHub 的真實標頭和錯誤回應本文,以及你 SDK 的重試政策。 你的應用程式也需要一個只供測試使用的開關來連線到模擬環境。
呼叫真正的 API,直到它限制了你 實際行為 它最多會用到 5,000 次請求,且你無法按需觸發次級限制,且你的整合可能遭到封鎖
攔截應用程式的真實流量,並按需返回速率限制回應 真實網址、真實的 SDK 與重試政策,以及 GitHub 自有的標頭和錯誤格式 你的應用程式中的任何內容都不會改變,所以它不會在隔離狀態下測試你的程式碼。 那個就留給你的單元測試吧。

在你的應用程式中試試看

Dev Proxy 會攔截你應用程式對 api.github.com 的請求,並回傳 GitHub 式的速率限制回應,而你的應用程式則持續呼叫真實的 URL。 github-rate-limiting 預設設定會將你的請求計入每小時 60 次的限制額度,傳送 x-ratelimit-* 標頭,並在你用完額度時回傳 429,並附帶 API rate limit exceeded。在那之前,你的請求會被送到 GitHub,並計入你的實際限制。

下載預設集,並用它啟動 Dev Proxy:

devproxy config get github-rate-limiting
devproxy --config-file "~dataFolder/configs/github-rate-limiting/.devproxy/devproxyrc.json"

要測試次要限制,建議改為從同一個資料夾使用 devproxyrc-secondary.json 啟動 Dev Proxy。 它會隨機回傳次級速率限制retry-after,並帶有429標頭。

然後照常執行你的應用程式,觀察它的表現。 若要安裝 Dev Proxy,請參見 設定 Dev Proxy。

下一步

也請參閱