.NET Core 3.0之深刻源碼理解HttpClientFactory之實戰

 

寫在前面

前面兩篇文章透過源碼角度,理解了HttpClientFactory的內部實現,當咱們在項目中使用時,總會涉及如下幾個問題:html

  • HttpClient超時處理以及重試機制
  • HttpClient熔斷器模式的實現
  • HttpClient日誌記錄與追蹤鏈

接下來咱們將從使用角度對上述問題做出說明。git

詳細介紹

如下代碼參考了MSDN,由於代碼裏展現的GitHub接口確實能夠調通,省的我再寫一個接口出來測試了。github

HttpClient超時處理和重試機制

在此以前,咱們須要瞭解一下Polly這個庫,Polly是一款基於.NET的彈性及瞬間錯誤處理庫, 它容許開發人員以順暢及線程安全的方式執行重試(Retry),斷路器(Circuit),超時(Timeout),隔板隔離(Bulkhead Isolation)及後背策略(Fallback)。json

如下代碼描述了在.NET Core 3.0中如何使用超時機制。api

 1: Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10))

那麼如何將其註冊到對應的HttpClient實例呢,有不少種方式:安全

  • 經過AddPolicyHandler註冊
 1: services.AddHttpClient("github", c =>
 2:             {
 3:                 c.BaseAddress = new Uri("https://api.github.com/");
 4:  
 5:                 c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
 6:                 c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
 7:             }).AddPolicyHandler(Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10)));
  • 聲明Policy註冊對象,並將超時策略對象添加進去
 1: var registry = services.AddPolicyRegistry();
 2: var timeout = Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10));
 3: registry.Add("regular", timeout);

調用方式app

 1:  services.AddHttpClient("github", c =>
 2:             {
 3:                 c.BaseAddress = new Uri("https://api.github.com/");
 4:  
 5:                 c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json"); // GitHub API versioning
 6:                 c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample"); // GitHub requires a user-agent
 7:             }).AddPolicyHandlerFromRegistry("regular")

Polly重試也很簡單async

 1: var policyRegistry = services.AddPolicyRegistry();
 2:  
 3: policyRegistry.Add("MyHttpRetry",HttpPolicyExtensions.HandleTransientHttpError().WaitAndRetryAsync(
3
,retryAttempt => TimeSpan.FromSeconds(
Math.Pow(2, retryAttempt)
)));

這裏的重試設置是在第一次調用失敗後,還會有三次機會繼續重試,每一個請求的時間間隔是指數級延遲。ide

重試功能除了可使用Polly實現外,還可使用DelegatingHandler,DelegatingHandler繼承自HttpMessageHandler,用於」處理請求、響應回覆「,本質上就是一組HttpMessageHandler的有序組合,能夠視爲是一個「雙向管道」。微服務

此處主要展現DelegatingHandler的使用方式,在實際使用中,仍然建議使用Polly重試。

 1: private class RetryHandler : DelegatingHandler
 2: {
 3:     public int RetryCount { get; set; } = 5;
 4:  
 5:     protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
 6:     {
 7:         for (var i = 0; i < RetryCount; i++)
 8:         {
 9:             try
 10:             {
 11:                 return await base.SendAsync(request, cancellationToken);
 12:             }
 13:             catch (HttpRequestException) when (i == RetryCount - 1)
 14:             {
 15:                 throw;
 16:             }
 17:             catch (HttpRequestException)
 18:             {
 19:                 // 五十毫秒後重試
 20:                 await Task.Delay(TimeSpan.FromMilliseconds(50));
 21:             }
 22:         }
 23:     }
 24: }

註冊方式以下:

 1: services.AddHttpClient("github", c =>
 2: {
 3:     c.BaseAddress = new Uri("https://api.github.com/");
 4:  
 5:     c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json"); // GitHub API versioning
 6:     c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample"); // GitHub requires a user-agent
 7: })
 8: .AddHttpMessageHandler(() => new RetryHandler());

HttpClient熔斷器模式的實現

若是很是瞭解Polly庫的使用,那麼熔斷器模式的實現也會很是簡單,

 1: var policyRegistry = services.AddPolicyRegistry();
 2:  
 3: policyRegistry.Add("MyCircuitBreaker",HttpPolicyExtensions.HandleTransientHttpError().CircuitBreakerAsync(handledEventsAllowedBeforeBreaking: 10,durationOfBreak: TimeSpan.FromSeconds(30)));

這裏的熔斷器設置規則是在連續10次請求失敗後,會暫停30秒。這個地方能夠寫個擴展方法註冊到IServiceCollection中。

HttpClient日誌記錄與追蹤鏈

日誌記錄這塊與追蹤鏈,咱們通常會經過request.Header實現,而在微服務中,十分關注相關調用方的信息及其獲取,通常的作法是經過增長請求Id的方式來肯定請求及其相關日誌信息。

實現思路是增長一個DelegatingHandler實例,用以記錄相關的日誌以及請求鏈路

 1:     public class TraceEntryHandler : DelegatingHandler
 2:     {
 3:         private TraceEntry TraceEntry { get; set; }
 4:  
 5:         public TraceEntryHandler(TraceEntry traceEntry)
 6:         {
 7:             this.TraceEntry = traceEntry;
 8:         }
 9:  
 10:        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
 11:         {
 12:             request.Headers.TryAddWithoutValidation("X-TRACE-CID", this.TraceEntry.ClientId);
 13:             request.Headers.TryAddWithoutValidation("X-TRACE-RID", this.TraceEntry.RequestId);
 14:             request.Headers.TryAddWithoutValidation("X-TRACE-SID", this.TraceEntry.SessionId);
 15:             if (this.TraceEntry.SourceIP.IsNullOrEmpty())
 16:             {
 17:                 request.Headers.TryAddWithoutValidation("X-TRACE-IP", this.TraceEntry.SourceIP);
 18:             }
 19:  
 20:             if (this.TraceEntry.SourceUserAgent.IsNullOrEmpty())
 21:             {
 22:                 request.Headers.TryAddWithoutValidation("X-TRACE-UA", this.TraceEntry.SourceUserAgent);
 23:             }
 24:  
 25:             if (this.TraceEntry.UserId.IsNullOrEmpty())
 26:             {
 27:                 request.Headers.TryAddWithoutValidation("X-TRACE-UID", this.TraceEntry.UserId);
 28:             }
 29:  
 30:             return base.SendAsync(request, cancellationToken);
 31:         }
 32:     }

我在查找相關資料的時候,發現有個老外使用CorrelationId組件實現,做爲一種實現方式,我決定要展現一下,供你們選擇:

 1: public class CorrelationIdDelegatingHandler : DelegatingHandler
 2: {
 3:     private readonly ICorrelationContextAccessor correlationContextAccessor;
 4:     private readonly IOptions<CorrelationIdOptions> options;
 5:  
 6:     public CorrelationIdDelegatingHandler(
 7:         ICorrelationContextAccessor correlationContextAccessor,
 8:         IOptions<CorrelationIdOptions> options)
 9:     {
 10:         this.correlationContextAccessor = correlationContextAccessor;
 11:         this.options = options;
 12:     }
 13:  
 14:     protected override Task<HttpResponseMessage> SendAsync(
 15:         HttpRequestMessage request,
 16:         CancellationToken cancellationToken)
 17:     {
 18:         if (!request.Headers.Contains(this.options.Value.Header))
 19:         {
 20:             request.Headers.Add(this.options.Value.Header, correlationContextAccessor.CorrelationContext.CorrelationId);
 21:         }
 22:  
 23:         // Else the header has already been added due to a retry.
 24:  
 25:         return base.SendAsync(request, cancellationToken);
 26:     }
 27: }

參考連接:

https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/http-requests?view=aspnetcore-3.0

https://rehansaeed.com/optimally-configuring-asp-net-core-httpclientfactory/

原文出處:https://www.cnblogs.com/edison0621/p/11298882.html

相關文章
相關標籤/搜索