• .NET Core 3.0之深入源码理解HttpClientFactory之实战


     

    写在前面

    前面两篇文章透过源码角度,理解了HttpClientFactory的内部实现,当我们在项目中使用时,总会涉及以下几个问题:

    • HttpClient超时处理以及重试机制
    • HttpClient熔断器模式的实现
    • HttpClient日志记录与追踪链

    接下来我们将从使用角度对上述问题作出说明。

    详细介绍

    以下代码参考了MSDN,因为代码里展示的GitHub接口确实可以调通,省的我再写一个接口出来测试了。

    HttpClient超时处理和重试机制

    在此之前,我们需要了解一下Polly这个库,Polly是一款基于.NET的弹性及瞬间错误处理库, 它允许开发人员以顺畅及线程安全的方式执行重试(Retry),断路器(Circuit),超时(Timeout),隔板隔离(Bulkhead Isolation)及后背策略(Fallback)。

    以下代码描述了在.NET Core 3.0中如何使用超时机制。

       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);

    调用方式

       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重试也很简单

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

    这里的重试设置是在第一次调用失败后,还会有三次机会继续重试,每个请求的时间间隔是指数级延迟。

    重试功能除了可以使用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/

  • 相关阅读:
    参考资料
    利用docker compose启动gitlab及runner
    在gitlab上setup CI
    git ssh端口号变更之后所需要的修改
    使用Docker Image跑Gitlab
    用Docker Compose启动Nginx和Web等多个镜像
    .NET core mvc on Docker
    ubuntu 挂载windows共享目录的方法
    13-14 元旦随想
    Uva 10177 (2/3/4)-D Sqr/Rects/Cubes/Boxes?
  • 原文地址:https://www.cnblogs.com/edison0621/p/11298882.html
Copyright © 2020-2023  润新知