ASP.NET Core 2.1 中的 HttpClientFactory (Part 4) 整合Polly實現瞬時故障處理

原文:https://www.stevejgordon.co.uk/httpclientfactory-using-polly-for-transient-fault-handling
發表於:2018年6月git

      在本系列的上一篇文章中,我介紹了使用命名和類型客戶端註冊的DelegatingHandlers的傳出中間件的概念。儘管可使用該方法,但ASP.NET團隊但願在大多數狀況下,咱們無需手動構建本身的處理程序。在某些狀況下,庫的內置功能可能會提供咱們須要的功能。例如,有時將請求包裝在時序代碼中以跟蹤它們執行所需的時間有時會頗有用。如今,它已做爲默認日誌記錄的一部份內置到IHttpClientFactory中。在其餘狀況下,第三方集成可能會提供您所需的功能。例如,基於橫切面的思想,在HTTP請求期間處理瞬態故障。在這種狀況下,與其製做本身的重試邏輯,不如使用Polly之類的庫。
      Polly是一個流行的瞬態故障處理庫,它提供了一種機制來定義在發生某些故障時能夠應用的策略。重試策略是最經常使用的策略之一。您能夠封裝一些代碼,若是發生故障,將重試這些代碼,有時須要屢次重試。這在您的應用程序須要與外部服務進行通訊的狀況下很是有用。經過HTTP之類的傳輸工具與服務進行通訊時,始終存在瞬態故障的風險。暫時性故障可能會阻止您的請求完成,但也多是暫時的問題。在這種狀況下,使用重試是明智的選擇。
      除重試外,Polly還提供了許多其餘類型的策略,您可能但願將其中許多策略與重試結合使用,以創建處理故障的複雜方法。我將在本文中介紹一些常見的示例,可是若是您想更全面地介紹,我建議您查看Polly Wiki。
      ASP.NET團隊與Polly的主要維護者Dylan和Joel緊密合做,加入了一種集成模式,以使將Polly策略應用於HttpClient實例變得很是簡單。
      在使用Polly集成以前,咱們須要爲項目添加一個包引用。 IHttpClientFactory的常規功能位於Microsoft.Extensions.Http包中,該包做爲依賴項包含在Microsoft.AspNetCore.App 2.1元包中。這是ASP.NET Core 2.1中新的元數據包,其中不包含第三方依賴項。所以,爲了對IHttpClientFactory使用Polly擴展,咱們須要將Microsoft.Extensions.Http.Polly包添加到咱們的項目中。
      完成以後,csproj文件將以下所示:github

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp2.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.App" />
    <PackageReference Include="Microsoft.Extensions.Http.Polly" Version="2.1.0" />
  </ItemGroup>

</Project>

應用策略

      Microsoft.Extensions.Http.Polly程序包在IHttpClientBuilder上包含一個名爲AddPolicyHandler的擴展方法,咱們可使用該方法添加一個處理程序,該處理程序會將使用該客戶端實例的全部請求包裝在Polly策略中。當咱們定義一個命名或類型的客戶端時,將返回IHttpClientBuilder。
      咱們能夠在ConfigureServices方法中使用擴展方法…安全

services.AddHttpClient("github")
  .AddPolicyHandler(Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10)));

  在此示例中,咱們定義了一個名爲「 github」的客戶端,而且使用了AddPolicyHandler方法來傳遞超時策略。您在此處提供的策略必須是IAsyncPolicy <HttpResponseMessage>。此政策將在10秒後使全部請求超時。app

重用策略

      在可能的狀況下,使用Polly時,最好一次性的定義策略並在應用相同策略的狀況下共享它們。這樣,要更改策略規則,這些更改僅須要在一個地方進行。此外,它確保僅分配策略一次。固然,若是多個調用者但願經過同一斷路器實例運行,則必須共享諸如斷路器之類的策略。
      在此示例中,咱們將從上一個示例中聲明一次超時策略,並與兩個命名的客戶端共享該策略。工具

var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10));

services.AddHttpClient("github")
  .AddPolicyHandler(timeoutPolicy);

services.AddHttpClient("google")
  .AddPolicyHandler(timeoutPolicy);

  咱們將在本文後面介紹另外一種策略重用的選項,使用PolicyRegistry。ui

 瞬時故障處理

      在處理HTTP請求時,咱們要處理的最多見狀況是瞬時錯誤。因爲這是一個常見的要求,所以Microsoft.Extensions.Http.Polly軟件包包括一個特定的擴展名,咱們可使用該擴展名快速設置處理瞬態故障的策略。
      例如,要在命名客戶端的請求發生瞬時故障時添加基本重試,咱們能夠按如下方式註冊重試策略:google

services.AddHttpClient("github")
  .AddTransientHttpErrorPolicy(p => p.RetryAsync(3));

      在這種狀況下,當知足某些失敗條件時,將重試經過客戶端發出的全部請求。 AddTransientHttpErrorPolicy方法採用Func <PolicyBuilder <HttpResponseMessage>,IAsyncPolicy <HttpResponseMessage >>。這裏的PolicyBuilder將被預先配置爲處理HttpRequestExceptions,任何返回5xx狀態碼的響應以及任何帶有408(請求超時)狀態碼的響應。這應該適合許多狀況。若是您要求該策略在其餘條件下應用,則須要使用其餘重載來傳遞更具體的策略。
      衆所周知,重試時咱們須要考慮冪等性。重試HTTP GET是一個很是安全的操做。若是咱們撥打了電話但未收到任何回覆,則能夠安全地重試該電話,而不會形成任何危險。可是,請考慮若是咱們重試HTTP POST請求,可能會發生什麼。咱們必須格外當心,由於有可能實際上收到了您的原始請求,可是咱們收到的回覆提示失敗。在這種狀況下,重試可能致使數據重複或損壞下游系統中存儲的數據。在這裏,您須要更多地瞭解若是下游服務屢次收到相同的請求,下游服務將如何處理。重試操做安全嗎?當您擁有下游服務時,更容易控制它。例如,您可能使用一些惟一的標識符來防止重複的POST。
      當您沒法控制遊系統或知道重複的POST可能會帶來負面影響時,您將須要更仔細地控制策略。更合適的選擇多是定義不一樣的命名/類型客戶端。您能夠爲沒有反作用與有反作用的請求分別建立一個客戶端。而後,您可使用正確的客戶端來執行操做。可是,這可能會變得有點難以管理。更好的選擇是使用AddPolicyHandler的重載,該重載使咱們能夠訪問HttpRequestMessage,以即可以有條件地應用策略。該重載以下所示:
      AddPolicyHandler(Func<HttpRequestMessage, IAsyncPolicy<HttpResponseMessage>> policySelector)
      您會注意到,這裏的policySelector委託能夠訪問HttpRequestMessage,而且應該返回IAsyncPolicy <HttpResponseMessage>。咱們沒法像前面的示例那樣訪問PolicyBuilder設置來處理瞬態故障。若是要處理常見的暫時性錯誤,則須要爲咱們的政策定義預期條件。爲了使此操做更容易,Polly項目包含一個幫助程序擴展,咱們可使用它來設置一個PolicyBuilder,以處理常見的瞬時錯誤。要使用擴展方法,咱們須要從Nuget添加Polly.Extensions.Http包。
      而後,咱們能夠調用HttpPolicyExtensions.HandleTranisentHttpError()來獲取配置有瞬態故障條件的PolicyBuilder。咱們可使用PolicyBuilder建立合適的重試策略,而後在請求爲HTTP GET時有條件地應用該策略。在此示例中,任何其餘HTTP方法都使用NoOp策略。spa

var retryPolicy = HttpPolicyExtensions
  .HandleTransientHttpError()
  .RetryAsync(3);

var noOp = Policy.NoOpAsync().AsAsyncPolicy<HttpResponseMessage>();

services.AddHttpClient("github")
  .AddPolicyHandler(request => request.Method == HttpMethod.Get ? retryPolicy : noOp);

使用PolicyRegistry

      最後一個示例是如何經過策略註冊表(policy registry)應用策略的基本演示。爲了支持策略重用,Polly提供了PolicyRegistry的概念,該概念實質上是策略的容器。能夠在應用程序啓動時經過將策略添加到註冊表中來定義。而後能夠將註冊表傳遞出去,並經過名稱訪問策略。設計

      IHttpClientBuilder的擴展方法支持使用註冊表(registry)將基於Polly的處理程序添加到客戶端。日誌

var registry = services.AddPolicyRegistry();

var timeout = Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10));
var longTimeout = Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(30));

registry.Add("regular", timeout);
registry.Add("long", longTimeout);

services.AddHttpClient("github")
	.AddPolicyHandlerFromRegistry("regular");

  首先,咱們必須經過DI註冊一個PolicyRegistry。 Microsoft.Extensions.Http.Polly軟件包包括一些擴展方法,可簡化此過程。在上面的示例中,我調用了AddPolicyRegistry方法,該方法是IServiceCollection的擴展。這將建立一個新的PolicyRegistry並經過DI註冊,做爲IPolicyRegistry <string>和IReadOnlyPolicyRegistry <string>的實現。該方法返回策略,以便咱們能夠向其添加策略。

      在此示例中,咱們添加了兩個超時策略併爲其指定了名稱。如今,在註冊客戶端時,咱們能夠調用IHttpClientBuilder上可用的AddPolicyHandlerFromRegistry方法,使用的是策略名稱。當工廠建立命名客戶端實例時,它會添加適當的處理程序,並調用「regular」重試策略。

總結

      做爲Polly的老用戶,我很高興看到IHttpClientFactory添加了這些集成。這些庫整合在一塊兒,可以無縫處理瞬態故障,HttpClient實例很是容易啓動和運行。我展現的示例很是基礎和籠統,但我但願它們能爲如何使用和註冊策略提供一個思路。有關Polly文檔和示例的更多詳細信息,建議您查看Polly Wiki。在設計這種集成時,與ASP.NET和Polly團隊進行的一些早期討論很是使人高興,由於我可以提出策略註冊表擴展的有用性。

相關文章
相關標籤/搜索