譯者:王亮
作者:Polly 團隊
原文:http://t.cn/EhZ90oq
聲明:我翻譯技術文章不是逐句翻譯的,而是根據我自己的理解來表述的(包括標題)。其中可能會去除一些不影響理解但本人實在不知道如何組織的句子
譯者序:這是“Polly and HttpClientFactory”這篇Wiki文檔翻譯的中篇,你可以 點擊這里查看上篇。接下來的兩篇則是在這個基礎上進行加強。本篇(中篇)主要講如何在ASP.NET Core中通過HttpClientFactory配置Polly策略。如果你對ASP.NET Core 2.1新引入的HttpClient工廠還比較陌生,建議先閱讀我的另一篇文章 .NET Core中正確使用 HttpClient的姿勢,這有助於更好地理解本文。
下面主要講如何在ASP.NET Core中通過HttpClientFactory配置Polly策略。
使用 AddTransientHttpErrorPolicy
讓我們先回到上篇的例子:
services.AddHttpClient("GitHub", client =>
{
client.BaseAddress = new Uri("https://api.github.com/");
client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
})
.AddTransientHttpErrorPolicy(builder => builder.WaitAndRetryAsync(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10)
}));
這里用了一個新的AddTransientHttpErrorPolicy方法,它可以很方便地配置一個策略來處理下面這些典型的HTTP調用錯誤:
- 網絡錯誤(HttpRequestException 異常)
- HTTP狀態碼 5XX(服務器錯誤)
- HTTP狀態碼 408(請求超時)
AddTransientHttpErrorPolicy方法添加了一個策略,這個策略默認預配置了上面HTTP錯誤的過濾器。在builder => builder子句中,你可以定義策略如何處理這些錯誤,還可以配置Polly提供的其它策略,比如重試(如上例所示)、斷路或回退等。
在AddTransientHttpErrorPolicy中處理網絡錯誤、HTTP 5XX和HTTP 408是一種便捷的方式,但這不是必需的。如果此方法內置的錯誤過濾器不適合您的需要(你需要仔細考慮一下),您可以擴展它,或者構建一個完全定制的Polly策略。
擴展 AddTransientHttpErrorPolicy
AddTransientHttpErrorPolicy方法也可以從Polly的一個擴展包Polly.Extensions.Http中得到,它在上面的基礎上進行了擴展。例如下面配置的策略可以處理429狀態碼:
using Polly.Extensions.Http;
// ...
var policy = HttpPolicyExtensions
.HandleTransientHttpError() // HttpRequestException, 5XX and 408
.OrResult(response => (int)response.StatusCode == 429) // RetryAfter
.WaitAndRetryAsync(/* etc */);
使用典型Polly語法配置好的策略
Polly 還有另一個擴展方法是AddPolicyHandler,它的一個重載方法可以接收任意IAsyncPolicy參數,所以你可以用典型的Polly語法先定義好任意的一個策略(返回類型為IAsyncPolicy),然后再傳給AddPolicyHandler擴展方法。
下面這個例子演示了用AddPolicyHandler來添加一個策略,其中我們編寫了自己的錯誤處理策略:
var retryPolicy = Policy.Handle<HttpRequestException>()
.OrResult<HttpResponseMessage>(response => MyCustomResponsePredicate(response))
.WaitAndRetryAsync(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10)
}));
services.AddHttpClient("GitHub", client =>
{
client.BaseAddress = new Uri("https://api.github.com/");
client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
})
.AddPolicyHandler(retryPolicy);
類似的,你還可以配置其它策略,比如超時策略:
var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(10);
services.AddHttpClient(/* etc */)
.AddPolicyHandler(timeoutPolicy);
所有通過HttpClient的調用返回的都是一個HttpResponseMessage對象,因此配置的策略必須是IAsyncPolicy對象(譯注:HTTP請求返回的是HttpResponseMessage對象,Polly定義的策略是一個IAsyncPolicy對象,所以AddPolicyHandler方法接收的參數是這兩者的結合體IAsyncPolicy對象)。非泛型的IAsyncPolicy可以通過下面的方式轉換成泛型的IAsyncPolicy:
var timeoutPolicy = Policy.TimeoutAsync(10);
services.AddHttpClient(/* etc */)
.AddPolicyHandler(timeoutPolicy.AsAsyncPolicy<HttpResponseMessage>());
應用多個策略
所有策略配置的方法也可以鏈式地配置多個策略,例如:
services.AddHttpClient(/* etc */)
.AddTransientHttpErrorPolicy(builder => builder.WaitAndRetryAsync(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10)
}))
.AddTransientHttpErrorPolicy(builder => builder.CircuitBreakerAsync(
handledEventsAllowedBeforeBreaking: 3,
durationOfBreak: TimeSpan.FromSeconds(30)
));
多個策略被應用的順序
當您配置多個策略時(如上例所示),策略應用於從外部(第一次配置)到內部(最后配置)的順序依次調用。在上面的示例中,調用的順序是這樣的:
- 首先通過(外部)重試策略,該策略將依次:
- 通過(內部)斷路策略的調用,該策略將依次:
- 進行底層HTTP調用。
這個示例之所以用此順序的策略是因為當重試策略在兩次嘗試之間等待時,斷路器可能在其中一個時間段(1、5或10秒)內改變狀態(譯注:上面示例中斷路策略是出現3次異常就“休息”30分鍾)。斷路策略被配置在重試策略的內部,因此每執行一次重試就會執行其內部的斷路策略。
上面的例子應用了兩個策略(重試和斷路),任意數量的策略都是可以的。一個常見的多個策略組合可能是這樣的:重試、斷路和超時(“下篇”會有例子)。
對於那些熟悉Polly的策略包的人來說,使用上面的方式配置多個策略完全等同於使用策略包,也適用於所有“策略包的使用建議”(鏈接:http://t.cn/EhJ4jfN)。
動態選擇策略
AddPolicyHandler的重載方法允許你根據HTTP請求動態選擇策略。
其中一個用例是對非等冪的操作應用不同的策略行為(譯注:“等冪“指的是一個操作重復使用,始終都會得到同一個結果)。對於HTTP請求來說,POST操作通常不是冪等的(譯注:比如注冊),PUT操作應該是冪等的。所以對於給定的API可能不是一概而論的。比如,您可能想要定義一個策略,讓它只重試GET請求,但不重試其他HTTP謂詞,比如這個示例:
var retryPolicy = HttpPolicyExtensions
.HandleTransientHttpError()
.WaitAndRetryAsync(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10)
});
var noOpPolicy = Policy.NoOpAsync().AsAsyncPolicy<HttpResponseMessage>();
services.AddHttpClient(/* etc */)
// 如果是GET請求,則使用重試策略,否則使用空策略
.AddPolicyHandler(request => request.Method == HttpMethod.Get ? retryPolicy : noOpPolicy);
上面的空策略會被應用於所有非GET的請求。空策略只是一種占坑模式,實際上不做任何事情。
從策略的注冊池中選擇策略
Polly還提供了策略注冊池(請參閱:http://t.cn/Ehi1SQp ),它相當於策略的存儲中心,被注冊的策略可以讓你在應用程序的多個位置重用。AddPolicyHandler的一個重載方法允許您從注冊池中選擇策略。
下面的示例使用IServiceCollection添加一個策略注冊池服務,向注冊池中添加一些策略,然后使用注冊池中的不同策略定義兩個調用邏輯。
var registry = services.AddPolicyRegistry();
registry.Add("defaultretrystrategy",
HttpPolicyExtensions.HandleTransientHttpError().WaitAndRetryAsync(/* etc */));
registry.Add("defaultcircuitbreaker",
HttpPolicyExtensions.HandleTransientHttpError().CircuitBreakerAsync(/* etc */));
services.AddHttpClient(/* etc */)
.AddPolicyHandlerFromRegistry("defaultretrystrategy");
services.AddHttpClient(/* etc */)
.AddPolicyHandlerFromRegistry("defaultretrystrategy")
.AddPolicyHandlerFromRegistry("defaultcircuitbreaker");
這個示例演示了從注冊池中選擇一個或多個策略應用在不同的HttpClient上,同一個策略被重復使用了兩次。策略注冊池的更復雜用例包括從外部動態更新注冊池中的策略,以便在運行期間動態重新配置策略(請查閱 http://t.cn/Ehidgqy 了解更多)。
相關閱讀: