.NET Core 3.0 HttpClientFactory-depth understanding of the actual source

 

EDITORIAL

The previous two articles through the source point of view, to understand the internal implementation HttpClientFactory when we use in the project, there will always involve the following questions:

  • HttpClient out processing and the retry mechanism
  • Fuse mode achieve HttpClient
  • HttpClient logging and tracking chain

Next we will make a description of the above-mentioned problems using angles.

Details

The following code reference to the MSDN , because the code is on display in the interface can indeed transferred through GitHub, the province I come out to write an interface testing.

HttpClient out processing and the retry mechanism

Prior to this, we need to look at this library Polly, Polly is based on a flexible and instantaneous .NET error-handling library that allows developers to smoothly and thread-safe way to perform retries (Retry), circuit breaker (Circuit) timeout (timeout), bulkhead isolation (bulkhead isolation) and back policy (Fallback).

The following code describes how to use a timeout mechanism in .NET Core 3.0 in.

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

So how to register it with the corresponding HttpClient instance of it, there are many ways:

  • By registering 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)));
  • Registration statement Policy objects and objects added to it timeout policy
   1:  var registry = services.AddPolicyRegistry();
   2:  var timeout = Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10));
   3:  registry.Add("regular", timeout);

Called

   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 is also very simple to retry

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

Retry setting here is that after the first call fails, there will be three opportunities to continue to retry each request time interval is exponentially delay.

In addition to using the retry function Polly implemented, but may also be used DelegatingHandler, DelegatingHandler inherited from HttpMessageHandler, for "Process requests, replies response", is essentially a combination of a set of ordered HttpMessageHandler may be regarded as a "two-way conduit" .

The main show here use DelegatingHandler in actual use, it is recommended to use Polly try again.

   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:                   // retry after fifty milliseconds
  20:                  await Task.Delay(TimeSpan.FromMilliseconds(50));
  21:              }
  22:          }
  23:      }
  24:  }

Sign up as follows:

   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/

Guess you like

Origin www.cnblogs.com/edison0621/p/11298882.html