今天,我们深度研究一下IHttpClientFactory。
一、前言
最早,我们是在Dotnet Framework中接触到HttpClient。
HttpClient给我们提供了与HTTP交互的基本方式。但这个HttpClient在大量频繁使用时,也会给我们抛出两个大坑:一方面,如果我们频繁创建和释放HttpClient实例,会导致Socket套接字资源耗尽,原因是因为Socket关闭后的TIME_WAIT时间。这个问题不展开说,如果需要可以去查TCP的生命周期。而另一方面,如果我们创建一个HttpClient单例,那当被访问的HTTP的DNS记录发生改变时,会抛出异常,因为HttpClient并不会允许这种改变。
现在,对于这个内容,有了更优的解决方案。
从Dotnet Core 2.1开始,框架提供了一个新的内容:IHttpClientFactory。
IHttpClientFactory用来创建HTTP交互的HttpClient实例。它通过将HttpClient的管理和用于发送内容的HttpMessageHandler链分离出来,来解决上面提到的两个问题。这里面,重要的是管理管道终端HttpClientHandler的生命周期,而这个就是实际连接的处理程序。
除此之外,IHttpClientFactory还可以使用IHttpClientBuilder方便地来定制HttpClient和内容处理管道,通过前置配置创建出的HttpClient,实现诸如设置基地址或添加HTTP头等操作。
为防止非授权转发,这儿给出本文的原文链接:https://www.cnblogs.com/tiger-wang/p/13752297.html
先来看一个简单的例子:
public void ConfigureServices(IServiceCollection services){
services.AddHttpClient("WangPlus", c =>
{
c.BaseAddress = new Uri("https://github.com/humornif");
})
.ConfigureHttpClient(c =>
{
c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});
}
在这个例子中,当调用ConfigureHttpClient()或AddHttpMessageHandler()来配置HttpClient时,实际上是在向IOptions的实例HttpClientFactoryOptions添加配置。这个方法提供了非常多的配置选项,具体可以去看微软的文档,这儿不多说。
在类中使用IHttpClientFactory时,也是同样的方式:创建一个IHttpClientFactory的单例实例,然后调用CreateClient(name)创建一个具有名称WangPlus的HttpClient。
看下面的例子:
public class MyService{
private readonly IHttpClientFactory _factory;
public MyService(IHttpClientFactory factory)
{
_factory = factory;
}
public async Task DoSomething()
{
HttpClient client = _factory.CreateClient("WangPlus");
}
}
用法很简单。
下面,我们会针对CreateClient()进行剖析,来深入理解IHttpClientFactory背后的内容。
二、HttpClient & HttpMessageHandler的创建过程CreateClient()方法是与IHttpClientFactory交互的主要方法。
看一下CreateClient()的代码实现:
private readonly IOptionsMonitor<HttpClientFactoryOptions> _optionsMonitorpublic HttpClient CreateClient(string name)
{
HttpMessageHandler handler = CreateHandler(name);
var client = new HttpClient(handler, disposeHandler: false);
HttpClientFactoryOptions options = _optionsMonitor.Get(name);
for (int i = 0; i < options.HttpClientActions.Count; i++)
{
options.HttpClientActions[i](client);
}
return client;
}
代码看上去很简单。首先通过CreateHandler()创建了一个HttpMessageHandler的处理管道,并传入要创建的HttpClient的名称。
有了这个处理管道,就可以创建HttpClient并传递给处理管道。这儿需要注意的是disposeHandler:false,这个参数用来保证当我们释放HttpClient的时候,处理管理不会被释放掉,因为IHttpClientFactory会自己完成这个管道的处理。
然后,从IOptionsMonitor的实例中获取已命名的客户机的HttpClientFactoryOptions。它来自Startup.ConfigureServices()中添加的HttpClient配置函数,并设置了BaseAddress和Header等内容。
最后,将HttpClient返回给调用者。
理解了这个内容,下面我们来看看CreateHandler(name)方法,研究一下HttpMessageHandler管道是如何创建的。
readonly ConcurrentDictionary<string, Lazy<ActiveHandlerTrackingEntry>> _activeHandlers;;readonly Func<string, Lazy<ActiveHandlerTrackingEntry>> _entryFactory = (name) =>
{
return new Lazy<ActiveHandlerTrackingEntry>(() =>
{
return CreateHandlerEntry(name);
}, LazyThreadSafetyMode.ExecutionAndPublication);
};
public HttpMessageHandler CreateHandler(string name)
{
ActiveHandlerTrackingEntry entry = _activeHandlers.GetOrAdd(name, _entryFactory).Value;
entry.StartExpiryTimer(_expiryCallback);
return entry.Handler;
}
看这段代码:CreateHandler()做了两件事:
创建或获取ActiveHandlerTrackingEntry;
开始一个计时器。