1. 程式人生 > >在 .NET Core 中結合 HttpClientFactory 使用 Polly(下篇)

在 .NET Core 中結合 HttpClientFactory 使用 Polly(下篇)

譯者:王亮作者:Polly 團隊原文:http://t.cn/EhZ90oq宣告:我翻譯技術文章不是逐句翻譯的,而是根據我自己的理解來表述的(包括標題)。其中可能會去除一些不影響理解但本人實在不知道如何組織的句子

譯者序:這是“Polly and HttpClientFactory”這篇Wiki文件翻譯的下篇。你可以 點選這裡檢視上篇,和 點選這裡檢視中篇。本篇(下篇)主要講幾個Polly和HttpClientFactory在ASP.NET Core中結合使用的用例。如果你對ASP.NET Core 2.1新引入的HttpClient工廠還比較陌生,建議先閱讀我的另一篇文章 .NET Core中正確使用 HttpClient的姿勢

,這有助於更好地理解本文。

—— 正文 ——

下面主要講幾個Polly和HttpClientFactory在ASP.NET Core中結合使用的用例。

用例:應用超時策略

HttpClient已經有了一個Timeout屬性,但是在使用重試策略時該如何應用呢?Polly的超時策略又適用於什麼地方?

  • HttpClient.Timeout屬性設定的超時將被應用於HttpClient例項的所有呼叫,包括重試之間的所有嘗試和等待。

  • 要在每次重試中使用超時,就要在Polly的超時策略之前配置重試策略。

在這種情況下,你可能希望重試策略在每次單個超時時重試。為此,需要讓重試策略處理超時策略丟擲的TimeoutRejectedException異常。

下面這個示例使用了上篇提到的Polly.Extensions.Http這個包,它可以很方便地為Http錯誤(比如HttpRequestException、Http 5XX和Http 408等)新增額外的處理。

using Polly.Extensions.Http;var retryPolicy = HttpPolicyExtensions    .HandleTransientHttpError()    .Or<TimeoutRejectedException>() // 若超時則丟擲此異常    .WaitAndRetryAsync(new[]        {            TimeSpan.FromSeconds(1
), TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10) });// 為每個重試定義超時策略var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(10);serviceCollection.AddHttpClient("GitHub", client =>{ client.BaseAddress = new Uri("https://api.github.com/"); client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json"); client.Timeout = TimeSpan.FromSeconds(60); // 預設超時時間}).AddPolicyHandler(retryPolicy)// 將超時策略放在重試策略之內,每次重試會應用此超時策略.AddPolicyHandler(timeoutPolicy);

用例:快取策略

Polly 的快取策略可以在通過IHttpClientFactory配置的委託處理程式中使用。Polly是通用的(不與Http請求繫結),因此在編寫程式碼時,Polly快取策略從Polly.Context中確定要使用的快取鍵。可以通過HttpRequestMessage請求上的一個擴充套件方法來設定這個引數:

request.SetPolicyExecutionContext(new Polly.Context("CacheKeyToUseWithThisRequest"));

由於Polly快取策略是在HttpResponseMessage級別的委託代理服務上進行快取,因此還需要考慮下面的問題。

HttpResponseMessage級別的快取是否合適?

如果你想重用HttpResponseMessage,那麼在HttpResponseMessage級別上進行快取可能非常合適。

但在某些情況下,比如呼叫WebService來獲取一些序列化資料,然後反序列化到應用程式中的本地型別,HttpResponseMessage可能不是快取的最佳粒度。

在這些情況下,HttpResponseMessage級別上的快取意味著每次命中快取都會重複讀取資料流和反序列化,這在效能方面是不必要的。

在更高級別快取可能更合適——例如,快取流或反序列化到應用程式的本地型別的結果。

快取HttpResponseMessage還要考慮以下三點:

  • HttpResponseMessage可以包含HttpContent,它只能向前讀取流(只能讀取一次)。這可能意味著,當CachePolicy第二次從快取中檢索它時,除非重新初始化流指標,否則無法重新讀取流。
  • 考慮去個性化和時間戳。快取的個人特有資訊和時間戳可能不適合重新提供給後續的請求。
  • 注意只快取狀態碼為200(OK)的響應。考慮使用Response.EnsureSuccessStatusCode()等方法確保只有成功的響應才能傳遞給快取策略。或者你可以使用這裡(http://t.cn/Ehnr78P)描述的自定義ITtlStrategy。

用例:在策略執行和呼叫之間交換資訊

Polly策略的每次執行都會攜帶Polly.Context類的一個執行域例項(execution-scoped instance),該類的作用是提供上下文,並允許在執行前、執行中和執行後階段之間交換資訊(譯註:類似於HttpContext)。

對於通過HttpClientFactory和Polly配置的HttpClient,可以在執行之前使用擴充套件方法HttpRequestMessage.SetPolicyExecutionContext(context)來設定被用於Http呼叫的上下文Polly.Context。該上下文具有字典語義,允許您傳遞任意資料。

var context = new Polly.Context();context["MyCustomData"] = foo;HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUri);request.SetPolicyExecutionContext(context); var response = await client.SendAsync(request, cancellationToken);

Polly將該上下文例項作為輸入引數傳遞給策略上配置的任何委託鉤子(例如onRetry)。例如下面這個已經預先配置了策略的HttpClient:

var retryPolicy = HttpPolicyExtensions    .HandleTransientHttpError()    .WaitAndRetryAsync(new[]    {        TimeSpan.FromSeconds(1),        TimeSpan.FromSeconds(5),        TimeSpan.FromSeconds(10)    },    onRetryAsync: async (outcome, timespan, retryCount, ctx) => {        /* Do something with ctx["MyCustomData"] */        // ...    });

委託鉤子可以在執行期間設定其上下文資訊:

var retryPolicy = HttpPolicyExtensions    .HandleTransientHttpError()    .WaitAndRetryAsync(new[]    {        TimeSpan.FromSeconds(1),        TimeSpan.FromSeconds(5),        TimeSpan.FromSeconds(10)    },    onRetryAsync: async (outcome, timespan, retryCount, ctx) => {        ctx["RetriesInvoked"] = retryCount;        // ...    });

這些資訊可以在執行後從上下文中讀取:

var response = await client.SendAsync(request, cancellationToken);var context = response.RequestMessage?.GetPolicyExecutionContext(); // 如果還沒有儲存在區域性變數中if (context?.TryGetValue("RetriesInvoked", out int? retriesNeeded) ?? false){    // Do something with int? retriesNeeded}

注意,只有在執行之前使用HttpRequestMessage.SetPolicyExecutionContext(context)設定了上下文時,HttpRequestMessage.GetPolicyExecutionContext()的獲得的上下文才可用。

相關閱讀: