其他分享
首页 > 其他分享> > 测试CSS兼容性:HttpClientFactory 使用说明 及 对 HttpClient 的回顾和对比

测试CSS兼容性:HttpClientFactory 使用说明 及 对 HttpClient 的回顾和对比

作者:互联网

HttpClient 日常使用及坑点:

在C#中,平时我们在使用HttpClient的时候,会将HttpClient包裹在using内部进行声明和初始化,如:

using(var httpClient = new HttpClient())
{
    //other codes
}

至于为什么?无外乎是:项目代码中就是这样写的,依葫芦画瓢/别人就是这样用的/在微软官方的ASP.NET教程中也是这么干的。

说的技术范点:当你使用继承了IDisposable接口的对象时,建议在using代码块中声明和初始化,当using代码段执行完成后,会自动释放该对象而不需要手动进行显示Dispose操作。

但这里,HttpClient这个对象有点特殊,虽然继承了IDisposable接口,但它是可以被共享的(或者说可以被复用),且线程安全。从项目经验来看,倒是建议在整个应用的生命周期内,复用HttpClient实例,而不是每次RPC请求的时候就实例化一个。(之前在优化公司一个web项目的时候,也曾经因为HttpClient载过一次坑,后面我会进行简述。)

我们先来用个简单的例子做下测试,看为什么不要每次RPC请求都实例化一个HttpClient:

  public class Program
    {
        static void Main(string[] args)
        {
            HttpAsync();
            Console.WriteLine("Hello World!");
            Console.Read();
        }

        public static async void HttpAsync()
        {
            for (int i = 0; i < 10; i++)
            {
                using (var client = new HttpClient())
                {
                    var result = await client.GetAsync("http://www.baidu.com");
                    Console.WriteLine($"{i}:{result.StatusCode}");
                }
            }
        }
    }

运行项目输出结果后,通过netstate查看下TCP连接情况:

e92bde19-eae0-41a4-83ed-08c2038fdd0f.png

#使用jemter压测复现错误信息:
Unable to connect to the remote serverSystem.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted.

说白话:就是会出现“各种套接字问题”。(码WCF的童鞋可能更加记忆尤新,问题追根溯源都是换汤不换药。)

熊厂里面能够搜索出来的解决方法,基本都是“减少超时时间”,但人为减少了超时时间会出现各种莫名其妙的错误。且无法避免服务器迟早崩溃的问题。

可以通过注册表进行修改默认值:[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpTimedWaitDelay])

那么如何处理这个问题?答案已经在上面说了,“复用HttpClient”即可。如:

    public class Program
    {
        private static readonly HttpClient _client = new HttpClient();
        static void Main(string[] args)
        {
            HttpAsync();
            Console.WriteLine("Hello World!");
            Console.Read();
        }

        public static async void HttpAsync()
        {
            for (int i = 0; i < 10; i++)
            {
                var result = await _client.GetAsync("http://www.baidu.com");
                Console.WriteLine($"{i}:{result.StatusCode}");
            }
        }
    }

232f7b9e-b9f3-4444-9697-e0108c3b09fe.png

但这么调整HttpClient的引用后,依然存在一些问题可能会影响到你的项目(尚未影响到我:P),如:

那么有没有办法解决HttpClient的这些个问题?直到我遇到了 HttpClientFactory,瞬间写代码幸福感倍升,也感慨新时代的童鞋们真的太幸福了,老一辈踩的坑可以“完美”规避掉了。

HttpClientFactory优势:

HttpClientFactory 是ASP.NET CORE 2.1中新增加的功能。

从微软源码分析,HttpClient继承自HttpMessageInvoker,而HttpMessageInvoker实质就是HttpClientHandler。

HttpClientFactory 创建的HttpClient,也即是HttpClientHandler,只是这些个HttpClient被放到了“池子”中,工厂每次在create的时候会自动判断是新建还是复用。(默认生命周期为2min)

还理解不了的话,可以参考Task和Thread的关系,以前碰到HttpClient这个问题的时候,就一直在想微软什么时候官方出一个HttpClient的Factory,虽然时隔了这么多年直到.NET CORE 2.1才出,但也很是兴奋。

HttpClientFactory使用方法:

借助ASP.NET CORE MVC,可以很方便的进行HttpClient的使用

  1. 在Startup.cs中进行注册
    ```csharp
    public class Startup
    {
    public Startup(IConfiguration configuration)
    {
    Configuration = configuration;
    }

         public IConfiguration Configuration { get; }
    
         // This method gets called by the runtime. Use this method to add services to the container.
         public void ConfigureServices(IServiceCollection services)
         {
             //other codes
    
             services.AddHttpClient("client_1",config=>  //这里指定的name=client_1,可以方便我们后期服用该实例
             {
                 config.BaseAddress= new Uri("http://client_1.com");
                 config.DefaultRequestHeaders.Add("header_1","header_1");
             });
             services.AddHttpClient("client_2",config=>
             {
                 config.BaseAddress= new Uri("http://client_2.com");
                 config.DefaultRequestHeaders.Add("header_2","header_2");
             });
             services.AddHttpClient();
    
             //other codes
             services.AddMvc().AddFluentValidation();
         }
       }

    ```

  2. 使用,这里直接以controller为例,其他地方自行DI
    ```csharp
    public class TestController : ControllerBase
    {
    private readonly IHttpClientFactory _httpClient;
    public TestController(IHttpClientFactory httpClient)
    {
    _httpClient = httpClient;
    }

         public async Task<ActionResult> Test()
         {
             var client = _httpClient.CreateClient("client_1"); //复用在Startup中定义的client_1的httpclient
             var result = await client.GetStringAsync("/page1.html");
    
             var client2 = _httpClient.CreateClient(); //新建一个HttpClient
             var result2 = await client.GetStringAsync("http://www.site.com/XXX.html");
    
             return null;
         }
     }

    ```

实战用法2:使用自定义类执行HttpClientFactory请求

  1. 自定义HttpClientFactory请求类

    public class SampleClient
    {
        public HttpClient Client { get; private set; }
    
        public SampleClient(HttpClient httpClient)
        {
            httpClient.BaseAddress = new Uri("https://api.SampleClient.com/");
            httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
            httpClient.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
            Client = httpClient;
        }
    }
  2. 在Startup.cs中ConfigureService方法中注册SampleClient,代码如下,

    services.AddHttpClient<SampleClient>();
  3. 调用:

    public class ValuesController : Controller
    {
        private readonly SampleClient  _sampleClient;;
    
        public ValuesController(SampleClient  sampleClient)
        {
            _sampleClient = sampleClient;
        }
    
        [HttpGet]
        public async Task<ActionResult> Get()
        {
            string result = await  _sampleClient.client.GetStringAsync("/");
            return Ok(result);
        }
    }

实战用法3:完全封装HttpClient可以使用下面方法

  1. 自定义HttpClientFactory请求类

    public interface ISampleClient
    {
        Task<string> GetData();
    }
    
    public class SampleClient : ISampleClient
    {
        private readonly HttpClient _client;
    
        public SampleClient(HttpClient httpClient)
        {
            httpClient.BaseAddress = new Uri("https://api.SampleClient.com/");
            httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
            httpClient.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
            _client = httpClient;
        }
    
        public async Task<string> GetData()
        {
            return await _client.GetStringAsync("/");
        }
    }
  2. 在Startup.cs中ConfigureService方法中注册SampleClient,代码如下,

    services.AddHttpClient<ISampleClient, SampleClient>();
  3. 调用:

    public class ValuesController : Controller
    {
        private readonly ISampleClient  _sampleClient;;
    
        public ValuesController(ISampleClient  sampleClient)
        {
            _sampleClient = sampleClient;
        }
    
        [HttpGet]
        public async Task<ActionResult> Get()
        {
            string result = await _sampleClient.GetData();
            return Ok(result);
        }
    }

标签:SampleClient,HttpClientFactory,client,HttpClient,public,CSS,httpClient
来源: https://www.cnblogs.com/deepthought/p/11303015.html