Tip
第一次接觸節流? 了解什麼是限速(throttling)以及如何處理它。
一目了然
目標:測試你的應用程式如何處理 GitHub REST API 的速率限制
時間: 15分鐘
插件:RateLimitingPlugin、 GenericRandomErrorPlugin、 RetryAfterPlugin
前置條件:設定開發代理
你的應用程式呼叫 GitHub API。 它在你的機器上運作正常,接著 CI 工作、大型組織,或忙碌時就會把它推過速率限制,然後開始失敗。 要用真實 API 測試,你需要用盡速率限制配額,然後等上一小時才能再試。 Dev Proxy 在本地模擬 GitHub 的速率限制,使用你選擇的上限和時間視窗。
了解 GitHub 回傳什麼
GitHub 對 REST API 有兩種速率限制。
主要速率限制 是限制你每小時提出的請求數量。 例如,未認證請求為 60,個人存取權杖請求為 5,000。 每個回覆都包含顯示你所在位置的標頭:
| Header | Meaning |
|---|---|
x-ratelimit-limit |
每小時最大請求數 |
x-ratelimit-remaining |
目前視窗中剩餘的請求數量 |
x-ratelimit-reset |
窗口重置的時間,以 UTC Unix 時間戳秒數為單位 |
當你超過主要限制時,GitHub 會回傳403或429,並將x-ratelimit-remaining設為0。 在 x-ratelimit-reset 中顯示的時間之前,請勿重試。
次級速率限制可保護 GitHub 免於突增流量,例如同時請求過多或過快建立過多內容。 當你超出限制時,GitHub 會回傳 403 或 429,並顯示有關次要速率限制的訊息。 如果回應有 retry-after 標頭,就等那麼多秒。 否則,至少等待一分鐘,如果請求持續失敗,再延長等待時間。
GitHub 可以禁止那些在速率受限期間持續發送請求的整合。 更多資訊請參閱 GitHub 文件中的 REST API 速率限制。
模擬主要速率限制
使用 RateLimitingPlugin 來計數請求並回傳 GitHub 的速率限制標頭。 要測試而不等一小時,可以用小限額和短時間窗口。
檔案: devproxyrc.json
{
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/rc.schema.json",
"plugins": [
{
"name": "RetryAfterPlugin",
"enabled": true,
"pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll"
},
{
"name": "RateLimitingPlugin",
"enabled": true,
"pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
"configSection": "githubRateLimit"
}
],
"urlsToWatch": [
"https://api.github.com/*"
],
"githubRateLimit": {
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/ratelimitingplugin.schema.json",
"headerLimit": "x-ratelimit-limit",
"headerRemaining": "x-ratelimit-remaining",
"headerReset": "x-ratelimit-reset",
"resetFormat": "UtcEpochSeconds",
"costPerRequest": 1,
"rateLimit": 5,
"resetTimeWindowSeconds": 60,
"warningThresholdPercent": 0,
"whenLimitExceeded": "Custom",
"customResponseFile": "github-rate-limit-exceeded.json"
}
}
Caution
請將RetryAfterPlugin新增在RateLimitingPlugin之前的組態檔中。 如果你是在之後才加入,它的話,RateLimitingPlugin 會在 RetryAfterPlugin 檢查之前先處理該請求。
在自訂回應檔案中,定義 GitHub 在你超過主要速率限制時會回傳的回應。
檔案: github-rate-limit-exceeded.json
{
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/ratelimitingplugin.customresponsefile.schema.json",
"statusCode": 429,
"headers": [
{
"name": "content-type",
"value": "application/json; charset=utf-8"
}
],
"body": {
"message": "API rate limit exceeded for user ID 1.",
"documentation_url": "https://docs.github.com/rest/overview/rate-limits-for-the-rest-api"
}
}
啟動 Dev Proxy 並執行你的應用程式。
devproxy --config-file devproxyrc.json
Dev Proxy 會在每分鐘內將前 5 個請求轉發到 GitHub,並在回應上設定 x-ratelimit-* 標頭。 從第 6 次請求開始,Dev Proxy 會回傳速率限制的回應,將 x-ratelimit-remaining 設為 0,並將 x-ratelimit-reset 設為視窗結束。 如果你的應用程式在視窗重置前再次呼叫 API,RetryAfterPlugin 會回報此情況並限制該請求。
請檢查你的應用程式:
- 讀取
x-ratelimit-remaining並放慢速度,在它到達0之前。 - 在速率限制回應後停止呼叫 API,並等待
x-ratelimit-reset。 - 它會告訴使用者發生了什麼,例如「GitHub 速率限制已達,將於 14:05 重試」,而不是默默失敗。
Note
當你超過速率限制時,GitHub 會回傳 429 或 403。 要測試你的應用程式是否也能 403 處理,請改 statusCode 成 403。
RetryAfterPlugin它只追蹤429回應,所以不會回報在403之後的提前重試。
Tip
開發代理會將請求轉發到 GitHub,直到達到模擬的上限。 這些請求也會計入你真正的 GitHub 速率限制。
模擬次級速率限制
次級速率限制以突發形式出現,並包含標 retry-after 頭。 使用 GenericRandomErrorPlugin 隨機回傳這些資料。
檔案: devproxyrc.json
{
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/rc.schema.json",
"plugins": [
{
"name": "RetryAfterPlugin",
"enabled": true,
"pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll"
},
{
"name": "GenericRandomErrorPlugin",
"enabled": true,
"pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
"configSection": "githubSecondaryRateLimit"
}
],
"urlsToWatch": [
"https://api.github.com/*"
],
"githubSecondaryRateLimit": {
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/genericrandomerrorplugin.schema.json",
"errorsFile": "github-secondary-rate-limit.json",
"rate": 50,
"retryAfterInSeconds": 60
}
}
檔案: github-secondary-rate-limit.json
{
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/genericrandomerrorplugin.errorsfile.schema.json",
"errors": [
{
"request": {
"url": "https://api.github.com/*"
},
"responses": [
{
"statusCode": 429,
"headers": [
{
"name": "content-type",
"value": "application/json; charset=utf-8"
},
{
"name": "retry-after",
"value": "@dynamic"
}
],
"body": {
"message": "You have exceeded a secondary rate limit. Please wait a few minutes before you try again.",
"documentation_url": "https://docs.github.com/rest/overview/rate-limits-for-the-rest-api#about-secondary-rate-limits"
}
}
]
}
]
}
啟動 Dev Proxy 並執行你的應用程式。 檢查你的應用程式是否會等到 retry-after 標頭中指定的秒數後,才會再次呼叫 API。 如果沒有,RetryAfterPlugin 會回報。
如果你用 Octokit 搭配節流外掛,請檢查你 onRateLimit 和 onSecondaryRateLimit 處理器是否執行,且回傳的結果是否符合你的預期。
下一步
了解更多關於 RateLimitingPlugin。
也請參閱
- 模擬 Rate-Limit API 回應 - 任何 API 的速率限制
- 測試我的應用程式是否能正確處理限速 ——在任何 API 上進行限速
- RetryAfterPlugin - 驗證重試行為
- 在 CI/CD 中使用 Dev Proxy - 自動化你的管線韌性測試