• 使用 IHttpClientFactory 实现复原 HTTP 请求


    Polly 是一个 .NET 库,提供恢复能力和瞬态故障处理功能。 通过应用 Polly 策略(如重试、断路、隔离、超时和回退)即可实现这些功能。 

    断路:当系统出错的次数超过了指定的阈值,就要中断当前线路,等待一段时间后再继续。

    隔离:将可控的操作限制在一个固定大小的资源池中,以隔离有潜在可能相互影响的操作。

    HttpClient类实现 IDisposable,但在 using 语句中声明和实例化它并非首选操作,因为释放 HttpClient 对象时,基础套接字不会立即释放,这可能会导致套接字耗尽问题。 在长期运行的进程中使用 HttpClient 的共享实例时,开发人员会遇到另一个问题。 在将 HttpClient 实例化为单一实例或静态对象的情况下,它无法处理 DNS 更改,问题实际上不是 HttpClient 本身,而是 HttpClient 的默认构造函数,因为它创建了一个新的实际 HttpMessageHandler 实例,该实例具有上面提到的“套接字耗尽”和 DNS 更改问题 。

    要解决上述问题并使 HttpClient 实例可管理,.NET Core 2.1 引入了 IHttpClientFactory 接口,该接口可用于在应用中通过依赖关系注入 (DI) 来配置和创建 HttpClient 实例。 它还提供基于 Polly 的中间件的扩展,以利用 HttpClient 中的委托处理程序。

    使用 IHttpClientFactory 的好处

    同时实现 IHttpMessageHandlerFactory 的 IHttpClientFactory 当前实现具有以下优势:

    • 提供一个中心位置,用于命名和配置逻辑 HttpClient 对象。 例如,可以配置预配置的客户端(服务代理)以访问特定微服务。
    • 通过后列方式整理出站中间件的概念:在 HttpClient 中委托处理程序并实现基于 Polly 的中间件以利用 Polly 的复原策略。
    • HttpClient 已经具有委托处理程序的概念,这些委托处理程序可以链接在一起,处理出站 HTTP 请求。 将 HTTP 客户端注册到工厂后,可使用一个 Polly 处理程序将 Polly 策略用于重试、断路器等。
    • 管理 HttpMessageHandler 的生存期,避免在自行管理 HttpClient 生存期时出现上述问题。

    由于关联的 HttpMessageHandler 由工厂管理,因此可安全释放由 DI 注入的 HttpClient 实例。 事实上,注入的 HttpClient 实例是从 DI 角度区分范围的 。

    IHttpClientFactory (DefaultHttpClientFactory) 实现与 Microsoft.Extensions.DependencyInjection NuGet 包中的 DI 实现紧密关联。 

    结合使用类型化客户端和 IHttpClientFactory

    什么是“类型化客户端”? 它只是为某些特定用途预配置的 HttpClient。 此配置可以包括特定值,如基本服务器、HTTP 标头或超时。

    展示如何将类型化客户端与 IHttpClientFactory 结合使用的图表。

    安装包含 IServiceCollection 的 AddHttpClient 扩展方法的 Microsoft.Extensions.Http NuGet 包,在应用程序中添加 IHttpClientFactory。 此扩展方法用于注册内部 DefaultHttpClientFactory 类,后者用作接口 IHttpClientFactory 的单一实例。 它定义 HttpMessageHandlerBuilder 的临时配置。 此消息处理程序(HttpMessageHandler 对象)获取自池,可供从工厂返回的 HttpClient 使用。

    池中的 HttpMessageHandler 对象的生存期就是池中的 HttpMessageHandler 实例可重用的时间长度。 默认值为两分钟,但可基于每个类型化客户端重写此值。 要重写该值,请在创建客户端时在返回的 IHttpClientBuilder 上调用 SetHandlerLifetime(),如以下代码所示:

    //Set 5 min as the lifetime for the HttpMessageHandler objects in the pool used for the Catalog Typed Client
    services.AddHttpClient<ICatalogService, CatalogService>()
        .SetHandlerLifetime(TimeSpan.FromMinutes(5));

    还可以在注册中添加特定于实例的配置(例如,配置基址),并添加一些弹性策略,

    services.AddHttpClient<ICatalogService, CatalogService>(client =>
    {
        client.BaseAddress = new Uri(Configuration["BaseUrl"]);
    })
        .AddPolicyHandler(GetRetryPolicy())
        .AddPolicyHandler(GetCircuitBreakerPolicy());
    
    static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
    {
        return HttpPolicyExtensions
            .HandleTransientHttpError()
            .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
            .WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
    }

     GetRetryPolicy 用到了Microsoft.Extensions.Http.Polly 扩展包

     IHttpClientFactory允许你用命名的方式来配置和使用 HttpClient。

    services.AddHttpClient("GitHub", client =>
    {
        client.BaseAddress = new Uri("https://api.github.com/");
        client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
    })
    .AddTransientHttpErrorPolicy(builder => builder.WaitAndRetryAsync(new[]
    {
        TimeSpan.FromSeconds(1),
        TimeSpan.FromSeconds(5),
        TimeSpan.FromSeconds(10)
    }));
    
    public class MyController : Controller
    {
        private readonly IHttpClientFactory _httpClientFactory;
    
        public MyController(IHttpClientFactory httpClientFactory)
        {
            _httpClientFactory = httpClientFactory;
        }
    
        public Task<IActionResult> SomeAction()
        {
            var client = _httpClientFactory.CreateClient("GitHub");
            return Ok(await client.GetStringAsync("/someapi"));
        }
    }

    AddTransientHttpErrorPolicy方法也可以从Polly的一个扩展包Polly.Extensions.Http中得到

    Polly还提供了策略注册池,它相当于策略的存储中心,被注册的策略可以让你在应用程序的多个位置重用。AddPolicyHandler的一个重载方法允许您从注册池中选择策略。

    var registry = services.AddPolicyRegistry();
    
    registry.Add("defaultretrystrategy", 
        HttpPolicyExtensions.HandleTransientHttpError().WaitAndRetryAsync(/* etc */));
    registry.Add("defaultcircuitbreaker", 
        HttpPolicyExtensions.HandleTransientHttpError().CircuitBreakerAsync(/* etc */));
    
    services.AddHttpClient(/* etc */)
        .AddPolicyHandlerFromRegistry("defaultretrystrategy")
        .AddPolicyHandlerFromRegistry("defaultcircuitbreaker");

    将抖动策略添加到重试策略

    在高并发率、高可伸缩性和高争用的情况下,常规重试策略可能会对系统产生影响。 在部分运行中断的情况下,有可能会有许多客户端同时发出相似的重试操作,从而形成操作高峰,为克服这种情况,一个好办法是向重试算法或策略中添加抖动策略。 由于增加了指数退避的随机性,这可能会改进端到端系统的整体性能。 这样在出现问题时可以分散峰值。

    Random jitterer = new Random();
    var retryWithJitterPolicy = HttpPolicyExtensions
        .HandleTransientHttpError()
        .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
        .WaitAndRetryAsync(6,    // exponential back-off plus some jitter
            retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))  
                          + TimeSpan.FromMilliseconds(jitterer.Next(0, 100))
        );
  • 相关阅读:
    springboot打war包汇总
    springBoot获取@NotBlank,@NotNull注解的message信息
    springBoot数据校验与统一异常处理
    ETL子系统
    “斐波那契数列”衍生题
    什么是数据仓库?
    准确率、精确率、召回率、F-Measure、ROC、AUC
    python探索微信朋友信息
    Kaggle之泰坦尼克号幸存预测估计
    通过房价预测入门Kaggle
  • 原文地址:https://www.cnblogs.com/yetsen/p/13767519.html
Copyright © 2020-2023  润新知