• HttpClientFactory 是 HttpClient 的正确使用方式


    1.简介

    • 它的组件包是Microsoft.Extensions.Http
    • 复原HttpClient带来的问题

    HttpClient相关问题

    • 虽然HttpClient类实现了IDisposable,但不是首选在using语句中声明和实例化它,因为释放HttpClient对象时,基础套接字不会立即释放,这可能会导致“套接字耗尽”问题 。因此,HttpClient 应进行一次实例化并在应用程序的生命周期中重复使用。 在负载较重的情况下,实例化每个请求的 HttpClient 类将耗尽可用的套接字数。 该问题会导致 SocketException 错误。 要解决此问题,可能的方法是将 HttpClient 对象创建为单一对象或静态对象,在长期运行的进程中使用 HttpClient 的共享实例时,开发人员遇到的另一个问题。 在将 HttpClient 实例化为单一实例或静态对象的情况下,它无法处理 DNS 更改,但是,问题实际上不是 HttpClient 本身,而是 HttpClient 的默认构造函数,因为它创建了一个新的实际 HttpMessageHandler 实例,该实例具有上面提到的“套接字耗尽”和 DNS 更改问题 。

    参考解决内容

    核心点

    • 管理内部HttpMessageHandler的生命周期,灵活应对资源问题和DNS刷新问题。
    • 支持命名化,类型化配置,集中管理配置,避免冲突
    • 灵活的出站请求管道配置,轻松管理请求生命周期
    • 内置管道最外层和最内存日志记录器,有Information和Trace输出

    核心对象

    • HttpClient
    • HttpMessageHandler
    • SocketsHttpHandler
    • DelegatingHandler
    • IHttpClientFactory
    • IHttpClientBuilder

    2.使用方式

    • 基本用法
    • 命名客户端模式
    • 类型化客户端模式
    • 生成的客户端

    基本用法

    //首先,在Startup中的ConfigureService注册我们的HttClient服务,仅仅一行代码就可以搞定
    service.AddHttpClien();
    
    //接着,我们通过依赖注入来请求IHttpClientFactory,并创建HttpClient实例,以下代码之后我们就可以放心使用HttpClient
    private readonly IHttpClientFactory _clientFactory;
    public BasicUsageModel(IHttpClientFactory clientFactory)
    {
      _clientFactory = clientFactory;
    }
    public async Task OnGet()
    {
        var client = _clientFactory.CreateClient();
            client.BaseAddress = new Uri("http://api.github.com");
            string result = await client.GetStringAsync("/");
            return Ok(result);
        
    }
    

    命名式使用方式

    命名式的使用方式可以解决第一种方式的问题,对于特定域名的 HTTP 调用,可以只做一次配置,然后在多个地方通过名称拿到相同的 Client 来使用。

    首先在 Startup.cs 注册服务的地方添加带有特定名称的 HttpClient 服务,并添加需要的配置,如下示例:

    services.AddHttpClient();
    services.AddHttpClient("github", c =>
    {
        c.BaseAddress = new Uri("https://api.github.com/");
        c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
        c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
    });
    

    注意这里添加了两次 AddHttpClient,一次是没有命名的,一次是有命名的。在 Controller 中的使用方式是:

    public class ValuesController : BaseController
    {
        private readonly IHttpClientFactory _httpClientFactory;
    
        public ValuesController(IHttpClientFactory httpClientFactory)
        {
            _httpClientFactory = httpClientFactory;
        }
    
        [HttpGet]
        public async Task<ActionResult> Get()
        {
            var client = _httpClientFactory.CreateClient("github");
            string result = await client.GetStringAsync("/");
            return Ok(result);
        }
    }
    

    但如果使用很频繁,这种获取 Client 的方式还是有一点麻烦。下面介绍第三种方式,可以在使用的时候方便一点,但配置要麻烦些。

    类型化客户端

    public class GitHubClient
    {
        public HttpClient Client { get; private set; }
    
        public GitHubClient(HttpClient httpClient)
        {
            httpClient.BaseAddress = new Uri("https://api.github.com/");
            httpClient.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
            httpClient.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
            Client = httpClient;
        }
    
        public async Task<string> GetData()
        {
            return await _client.GetStringAsync("/");
        }
    }
    
    //注册
    services.AddHttpClient<GitHubClient>();
    

    然后在需要的地方(比如 Controller)中这样使用:

    public class ValuesController : BaseController
    {
        private readonly GitHubClient _gitHubClient;
    
        public ValuesController(GitHubClient gitHubClient)
        {
            _gitHubClient = gitHubClient;
        }
    
        [HttpGet]
        public async Task<ActionResult> Get()
        {
            string result = await _gitHubClient.GetData();
            return Ok(result);
        }
    }
    

    类型化的方式和命名式的方式相比,虽然在配置的时候会麻烦一点,但它有两个好处:一是不用通过名称字符串来获得 Client,而是直接通过依赖注入获得具体的 Client;二是可以在自定义的 HttpClient 类中扩展共用的方法。

    3.出站请求中间件

    • HttpClient具有委托处理程序概念。

    创建委托处理程序

    • 派生自DelegatingHandler (将 HTTP 响应消息的处理委托给另一处理程序(称为“内部处理程序”)的 HTTP 处理程序的类型。)
    • 重写SendAsync
     public class xxxHandler : DelegatingHandler
        {
            protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
            {
                //处理请求
               
                var result = await base.SendAsync(request, cancellationToken); //调用内部handler
    
                //处理响应
    
                return result;
            }
        }
          
     public void ConfigureServices(IServiceCollection services){
                 services.AddSingleton<xxxHandler>();  //创建xxxHandler服务生命周期
                services.AddHttpClient("XXXServiceClient", client =>
                {
                    client.DefaultRequestHeaders.Add("client-name", "namedclient");
                    client.BaseAddress = new Uri("https://localhost:5001");
                }).SetHandlerLifetime(TimeSpan.FromMinutes(20)) //SetHandlerLifetime设置生命周期时长,刷新DNS时间
                .AddHttpMessageHandler(provider => provider.GetService<xxxHandler>()); //这里添加 Handler
    }
    public class XXXServiceClient
        {
            IHttpClientFactory _httpClientFactory;
    
            const string _clientName = "NamedOrderServiceClient";  //定义客户端名称
    
            public XXXServiceClient(IHttpClientFactory httpClientFactory)
            {
                _httpClientFactory = httpClientFactory;
            }
            public async Task<string> Get()
            {
                var client = _httpClientFactory.CreateClient(_clientName); //使用客户端名称获取客户端
    
                //使用client发起HTTP请求,这里使用相对路径来访问
                return await client.GetStringAsync("/OrderService");
            }
        }
    

    生命周期

    管道模式

    本图来自极客时间-肖伟宇

    • 每次对 IHttpClientFactory 调用 CreateClient 都会返回一个新 HttpClient 实例。 每个命名客户端都创建一个 HttpMessageHandler。 工厂管理 HttpMessageHandler 实例的生存期。
    • 处理程序的默认生存期为两分钟,可通过SetHandlerLifetime 重新设置。
  • 相关阅读:
    es6
    ES6
    ES6
    css3
    滚动穿透的6种解决方案【已自测】
    css特效
    css布局
    css布局
    js
    【消灭代办】第5周
  • 原文地址:https://www.cnblogs.com/tangge/p/15194541.html
Copyright © 2020-2023  润新知