zoukankan      html  css  js  c++  java
  • .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/

  • 相关阅读:
    .NET Interop 工具集
    关于正弦波的算法
    Windows Phone 系列 本地数据存储
    Xaml cannot create an instance of “X”
    Windows Phone 系列 使用 MVVM绑定时无法获取当前值
    Windows Phone 系列 应用程序图标无法显示
    Windows Phone 系列 WPConnect无法上网的问题
    Windows Phone 系列 使用 Windows Phone 保存铃声任务
    WP7.5提交应用
    Windows Phone 系列 动态删除ObservableCollection
  • 原文地址:https://www.cnblogs.com/edison0621/p/11298882.html
Copyright © 2011-2022 走看看