Tip
第一次接觸節流? 了解什麼是限速(throttling)以及如何處理它。
一目了然
目標:確認你的 .NET HTTP 韌性處理器會如預期重試、退避並超時
時間: 15分鐘
插件:GenericRandomErrorPlugin、RetryAfterPlugin、LatencyPlugin
前置條件:設定 Dev Proxy,這是一個使用 Microsoft.Extensions.Http.Resilience 的 .NET 應用程式
你已將 AddStandardResilienceHandler() 新增至你的 HttpClient。 你怎麼知道它有效? 你呼叫的 API 很少能依需求刻意失敗,而模擬 HttpMessageHandler 的單元測試會跳過你想測試的韌性管線。
Dev Proxy 位於你的應用程式和 API 之間。 它會回傳錯誤和緩慢的回應給你的應用程式,所以你的應用程式無須修改即可執行,韌性處理器會對真實的 HTTP 回應做出反應。 你會在 Dev Proxy 輸出中看到每一次嘗試。
標準韌性處理常式的作用
在測試前,先知道會遇到什麼。 在預設選項下,AddStandardResilienceHandler():
| 行為 | 預設 |
|---|---|
| 重試已啟用 | HTTP 500 及以上、408、429、 HttpRequestException和 TimeoutRejectedException |
| 重試次數 | 3,並以指數式後退與抖動,從2秒開始 |
Retry-After 標頭 |
榮幸。 處理器會等待 API 所要求的時間。 |
| 嘗試超時 | 每次嘗試10秒 |
| 總逾時 | 請求時間30秒,包含所有重試 |
完整策略及其預設值列表,請參見 標準韌性處理者預設值。
透過 Dev Proxy 路由你的應用程式
.NET 使用系統代理,所以當你啟動 Dev Proxy 時,它會攔截應用程式的請求,且不需修改程式碼。 欲了解更多資訊,請參閱「使用 Dev Proxy 搭配 .NET 應用程式」。
模擬瞬態錯誤
建立一個開發代理設定,讓對 API 的請求因處理常式會重試的錯誤而失敗。 這個範例使用 https://api.contoso.com。 用你應用程式呼叫的 API URL 來取代它。
檔案: 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": "transientErrors"
}
],
"urlsToWatch": [
"https://api.contoso.com/*"
],
"transientErrors": {
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/genericrandomerrorplugin.schema.json",
"errorsFile": "transient-errors.json",
"rate": 50
}
}
Caution
請將RetryAfterPlugin新增在GenericRandomErrorPlugin之前的組態檔中。 如果你是在之後才加,GenericRandomErrorPlugin 會在 RetryAfterPlugin 檢查之前先讓該請求失敗。
在 error 檔案中,定義一個限速回應和兩個伺服器錯誤。
@dynamic 值會設定 Retry-After 標頭,並告訴 RetryAfterPlugin 檢查你的應用程式是否等了那麼久才再次呼叫 API。
檔案: transient-errors.json
{
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/genericrandomerrorplugin.errorsfile.schema.json",
"errors": [
{
"request": {
"url": "https://api.contoso.com/*"
},
"responses": [
{
"statusCode": 429,
"headers": [
{
"name": "Retry-After",
"value": "@dynamic"
}
]
},
{
"statusCode": 500
},
{
"statusCode": 503
}
]
}
]
}
啟動 Dev Proxy 並執行你的應用程式。
devproxy --config-file devproxyrc.json
失敗率高達 50%,大多數請求在一兩次重試後就能恢復。 在開發代理輸出中,請檢查:
- 在收到 429 回應後,對同一 URL 的下一次嘗試會在
Retry-After時間之後進行。 Dev Proxy 預設使用 5 秒。 如果你的應用程式太早呼叫 API,RetryAfterPlugin會回報此情況並限制該請求。 - 你的應用程式發送的重試次數不會超過你設定的數量。
- 你不想重試的請求,比如會建立紀錄的
POST,只會發送一次。 要排除它們,請使用options.Retry.DisableForUnsafeHttpMethods()或options.Retry.DisableFor(...)。
測試重試用完後會發生什麼
重試會隱藏短暫的失敗。 你也需要知道當 API 持續失敗時,你的應用程式會怎麼做。 以 100% 失敗率啟動 Dev Proxy:
devproxy --config-file devproxyrc.json --failure-rate 100
Dev Proxy 顯示每個請求有 4 次嘗試:原始請求和 3 次重試。 最後一次重試後,標準處理常式不會再拋出例外。 它會回傳你程式碼的最後錯誤回應。 看看你的應用程式怎麼處理它。 例如,EnsureSuccessStatusCode() 會引發 HttpRequestException,而 GetStringAsync() 也會引發例外。 確保你的應用程式顯示有用的訊息或採用備援方案,而不是當機或顯示通用錯誤。
測試逾時
慢速的 API 會在處理器中觸發不同的路徑。 測試時,可以用 LatencyPlugin 將回應延遲到超過 10 秒的嘗試逾時時間。
檔案: devproxyrc.json
{
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/rc.schema.json",
"plugins": [
{
"name": "LatencyPlugin",
"enabled": true,
"pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
"configSection": "slowApi"
}
],
"urlsToWatch": [
"https://api.contoso.com/*"
],
"slowApi": {
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/latencyplugin.schema.json",
"minMs": 11000,
"maxMs": 15000
}
}
啟動 Dev Proxy 並執行你的應用程式。 每次嘗試都會超過 10 秒,因此嘗試逾時會將其取消,而處理常式會重試。 30 秒後,總逾時會取消請求,你的程式碼會收到 TimeoutRejectedException。 檢查你的應用程式是否能偵測到並告訴使用者發生了什麼事。
Tip
要用你自己的韌性設定測試同樣的情況,請更改 AddStandardResilienceHandler(options => ...) 呼叫中的數值,並重新執行相同的開發代理設定。
下一步
了解更多關於在任何 API 上模擬限速的資訊。
也請參閱
- 使用 Dev Proxy 搭配 .NET 應用程式 - .NET 設定
- GenericRandomErrorPlugin - 完整參考資料
- RetryAfterPlugin - 驗證重試行為
- LatencyPlugin - 模擬緩慢回應
- 變更請求失敗率 - 調整請求失敗發生的頻率
- 在 CI/CD 中使用 Dev Proxy - 自動化你的管線韌性測試