HttpClientFactory的使用

HttpClient的优缺点

HttpClient这个对象有点特殊,虽然继承了IDisposable接口,但它是可以被共享的(或者说可以被复用),且线程安全。从项目经验来看,倒是建议在整个应用的生命周期内,复用HttpClient实例,而不是每次RPC请求的时候就实例化一个。
在高并发的情况下,连接来不及释放,socket被耗尽,耗尽之后就会出现一个错误:

#使用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.
复制代码

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

可以通过注册表进行修改默认值:[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}");
            }
        }
    }
  • 可以看到,原先10个连接变成了1给连接。(请不要在意两次示例的目标IP不同—SLB导致的,都是百度的ip)
  • 另外,因为复用了HttpClient,每次RPC请求的时候,实际上还节约了创建通道的时间,在性能压测的时候也是很明显的提升。将web项目的TPS从单台600瞬间提升到了2000+,页面请求时间也从1-3s减少至100-300ms

至于如何创建一个静态HttpClient进行复用,大家可以按项目实际来,如直接创建一个“全局”静态对象,或者通过各类DI框架来创建均可。
缺陷

  • 因为是复用的HttpClient,那么一些公共的设置就没办法灵活的调整了,如请求头的自定义。
  • 因为HttpClient请求每个url时,会缓存该url对应的主机ip,从而会导致DNS更新失效(TTL失效了)

HttpClientFactory优势:

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

  1. HttpClientFacotry很高效,可以最大程度上节省系统socket。
  2. Factory,顾名思义HttpClientFactory就是HttpClient的工厂,内部已经帮我们处理好了对HttpClient的管理,不需要我们人工进行对象释放,同时,支持自定义请求头,支持DNS更新等等等。

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

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

HttpClientFactory使用方法:

1.在.NET Core 2.1版本之后引入的 HttpClientFactory解决了HttpClient的所有痛点。有了 HttpClientFactory,我们不需要关心如何创建HttpClient,又如何释放它。通过它可以创建具有特定业务的HttpClient,而且可以很友好的和 DI 容器结合使用,更为灵活。

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

  //修改默认的生命周期
   services.AddHttpClient()
  .SetHandlerLifetime(TimeSpan.FromMinutes(5));

一、第一种使用方式

  • 在Startup.cs中进行注册
   //注册http请求服务
            services.AddHttpClient();
  • Httphelper请求辅助类中使用
/// <summary>
        /// 注入http请求
        /// </summary>
        private readonly IHttpClientFactory httpClientFactory;
        public HttpHelp(IHttpClientFactory _httpClientFactory)
        {
            httpClientFactory = _httpClientFactory;
        }

        // <summary>
        // Get请求数据
        // <para>最终以url参数的方式提交</para>
        // </summary>
        // <param name="parameters">参数字典,可为空</param>
        // <param name="requestUri">例如/api/Files/UploadFile</param>
        // <returns></returns>
        public async Task<string> Get(Dictionary<string, string> parameters, string requestUri, string token)
        {
            //从工厂获取请求对象
            var client = httpClientFactory.CreateClient();
            //添加请求头
            if (!string.IsNullOrWhiteSpace(token))
            {
                client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
            }
            client.DefaultRequestHeaders.Add("Content-Type", "application/json; charset=utf-8");
            //拼接地址
            if (parameters != null)
            {
                var strParam = string.Join("&", parameters.Select(o => o.Key + "=" + o.Value));
                requestUri = string.Concat(requestUri, '?', strParam);
            }
            client.BaseAddress = new Uri(requestUri);
            return client.GetStringAsync(requestUri).Result;
        }
  • 然后我们在Startup.cs对相对的类进行注册就可以了使用了。

二、使用命名客户端

  • 1.在Startup.cs中进行注册,这个注册可以存在多个。以创建名字区分
services.AddHttpClient("github", c =>
{
    c.BaseAddress = new Uri("https://xxxxxxx.com/");
    // Github API versioning
    c.DefaultRequestHeaders.Add("Content-Type", "application/json; charset=utf-8");
    // Github requires a user-agent
    c.DefaultRequestHeaders.Add("Authorization", "asfasfasdsgdsfsdfsdafasfas");
});
  • 2.使用方式和上面的一样只要
/// <summary>
        /// 注入http请求
        /// </summary>
        private readonly IHttpClientFactory httpClientFactory;
        public HttpHelp(IHttpClientFactory _httpClientFactory)
        {
            httpClientFactory = _httpClientFactory;
        }

        // <summary>
        // Get请求数据
        // <para>最终以url参数的方式提交</para>
        // </summary>
        // <param name="parameters">参数字典,可为空</param>
        // <param name="requestUri">例如/api/Files/UploadFile</param>
        // <returns></returns>
        public async Task<string> Get(Dictionary<string, string> parameters, string requestUri, string token)
        {
            //从工厂获取请求对象   声明自己创建哪一个httpClient客户端
            var client = httpClientFactory.CreateClient("github");
            //添加请求头
            if (!string.IsNullOrWhiteSpace(token))
            {
                client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
            }
            client.DefaultRequestHeaders.Add("Content-Type", "application/json; charset=utf-8");
            //拼接地址
            if (parameters != null)
            {
                var strParam = string.Join("&", parameters.Select(o => o.Key + "=" + o.Value));
                requestUri = string.Concat(requestUri, '?', strParam);
            }
            client.BaseAddress = new Uri(requestUri);
            return client.GetStringAsync(requestUri).Result;
        }

三、类型化客户端

  • 1.创建一个类
public class HttpClienService
{
    public HttpClient Client { get; }
    public HttpClienService(HttpClient client)
    {
        client.BaseAddress = new Uri("https://xxxx.com/");
        // GitHub API versioning
        client.DefaultRequestHeaders.Add("Authorization",
            "xxxxxxxxxxxx");
        // GitHub requires a user-agent
        client.DefaultRequestHeaders.Add("Content-Type",
            "application/json; charset=utf-8");
        Client = client;
    }    //这个下面就是编写自己方法,进行调用
}
  • 2.在Startup.cs中进行注册,这个注册可以存在多个。
services.AddHttpClient<classHttp>();
//注册之后,使用依赖注入的方式进行注入,进行使用。

不飞则已,一飞冲天;

不鸣则已,一鸣惊人。

——司马迁《史记》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值