前面兩篇文章透過源碼角度,理解了HttpClientFactory的內部實現,當咱們在項目中使用時,總會涉及如下幾個問題:html
接下來咱們將從使用角度對上述問題做出說明。git
如下代碼參考了MSDN,由於代碼裏展現的GitHub接口確實能夠調通,省的我再寫一個接口出來測試了。github
在此以前,咱們須要瞭解一下Polly這個庫,Polly是一款基於.NET的彈性及瞬間錯誤處理庫, 它容許開發人員以順暢及線程安全的方式執行重試(Retry),斷路器(Circuit),超時(Timeout),隔板隔離(Bulkhead Isolation)及後背策略(Fallback)。json
如下代碼描述了在.NET Core 3.0中如何使用超時機制。api
1: Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10))
那麼如何將其註冊到對應的HttpClient實例呢,有不少種方式:安全
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)));
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());
若是很是瞭解Polly庫的使用,那麼熔斷器模式的實現也會很是簡單,
1: var policyRegistry = services.AddPolicyRegistry();
2:
3: policyRegistry.Add("MyCircuitBreaker",HttpPolicyExtensions.HandleTransientHttpError().CircuitBreakerAsync(handledEventsAllowedBeforeBreaking: 10,durationOfBreak: TimeSpan.FromSeconds(30)));
這裏的熔斷器設置規則是在連續10次請求失敗後,會暫停30秒。這個地方能夠寫個擴展方法註冊到IServiceCollection中。
日誌記錄這塊與追蹤鏈,咱們通常會經過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