ASP.NET Core 2.1中出现一个新的HttpClientFactory功能,git
它有助于解决开发人员在使用HttpClient实例从其应用程序发出外部Web请求时可能遇到的一些常见问题。github
在.NETCore平台的2.1新增了HttpClientFactory,虽然HttpClient这个类实现了disposable,但使用它的时候用声明using包装块的方式
一般不是最好的选择。处理HttpClient
,底层socket套接字不会当即释放。该HttpClient类
是专为多个请求重复使用而建立的。须要不一样的基地址,不一样的HTTP标头和其余对请求个性化操做的场景时,须要手动管理多个HttpClient实例,为了简化HttpClient
实例管理,.NET Core 2.1提供了一个新的HTTPClientFactory - 它能够建立,缓存和处理HttpClient
实例。
缓存
用ASP.NET团队的话说:“an opinionated factory for creating HttpClient instances”(一个用于建立HttpClient实例的最佳实践的工厂),而且是ASP.NET Core 2.1发布的新功能。根据你们之前使用HttpClient的经验,您可能遇到一些困扰的问题,有时甚至没有意识到您有问题(只是在并发并不大的场景没触发而已)。安全
第一个问题是当你在代码中建立太多的HttpClients时,这反过来会产生两个问题......服务器
HttpClient实现了IDisposable,这一般会致使开发人员在使用IDisposable对象时遵循正常模式,在using块中建立它。这样能够确保一旦完成对象而且它已经超出范围,就能够正确销毁对象。并发
所以,最优的方法是重用HttpClient实例,以便也能够重用链接。HttpClient是一个可变对象,但只要你没有运行期改变它,它其实是线程安全的而且能够共享。所以,一种常见的方法是将其注册为具备DI框架的单例模式,或者建立包含static静态实例的对象。app
可是,这会产生新问题。以这种方式使用单个HttpClient将保持链接打开而且不遵照DNS生存时间(TTL)设置(总之就是同一个HttpClient实例只能有一个请求头,在被请求方发生更改时,因为是单例不能作个性化改变,不然致使其余请求失败)。如今链接将永远不会得到DNS更新,所以您正在与之通讯的服务器将永远不会更新其地址。在某些状况下,这是彻底有可能的,在以上这种状况下,您能够平衡许多主机,这些主机可能随着时间的推移而改变,或者可能使用Blue/Green 部署推出新服务。若是服务器消改变,则您的链接使用的IP可能再也不响应您经过单个HttpClient发出的请求。框架
因此须要咱们手动去管理每类服务器的HttpClient的实例来进行个性化请求头的构造和发起请求!
socket
HttpClientFactory旨在帮助您开始解决这些问题,并提供了一种新的机制来建立在幕后为咱们正确管理的HttpClient实例。它将为咱们“作管理HttpClient的事”,咱们能够专一于业务!虽然在参考HttpClient时提到了上述问题,但事实上问题的根源实际上发生在HttpClient上,HttpClient使用了HttpClientHandler。HttpClientFactory管理处理程序的生命周期,以便咱们有一个能够重用的池,同时还能够(Rotating)轮换它们以使DNS不会过期。url
使用HttpClient的昂贵部分其实是建立HttpClientHandler和链接。以这种HttpClientFacotry方式聚集这些内容意味着咱们能够更高效利用资源最节省地使用咱们系统上的socket。当您使用HttpClientFactory请求HttpClient时,实际上每次都会得到一个新实例,这意味着咱们没必要担忧会改变它的状态。此HttpClient可能(或可能不)使用池中的现有HttpClientHandler,从而使用现有打开的链接。
默认状况下,每一个新建立的HttpClientHandler(派生自HttpMessageHandler)生命周期只有2分钟。经过services.AddHttpClient()建立HttpClientFactory实例时,能够根据每个命名的Client客户机进行控制。达到生命周期后,处理程序将不会当即被释放掉,而是放入过时的池中。任何依赖于HttpClientFactory的处理程序链的客户端均可以继续使用它而没有任何问题。有一个后台做业检查过时的池,以查看处理程序的全部引用是否已在scope以外,此时能够将其释放掉。处理程序链过时后对新客户端的任何新请求都将得到新的处理程序链。
这种方法运行得至关不错,但.NET Core方面还有其余一些事情可能会进一步改善这种状况。.NET Core团队开发了一个新的ManagedHandler,它能够更正确地管理DNS,原则上能够保持更长时间,这意味着能够更有效地共享链接。这个新的处理程序还被设计为在不一样的操做系统中更加一致地运行。在该工做完成以前,上面的处理程序池是一个合理的解决方法。
咱们将首先建立一个简单的WebAPI项目
接下来,咱们须要转到咱们的Startup.cs文件并注册一个服务。
services.AddHttpClient();
services.AddScoped(typeof(ClassInService));//此处无关HttpClient,请暂时忽视他
在幕后,这将注册一些必需的服务,其中一个是IHttpClientFactory的实现。接下来,咱们在业务中使用他
public class ClassInService { /// <summary> /// 构建器 /// </summary> /// <param name="clientFactory"></param> public ClassInService(IHttpClientFactory clientFactory) { _clientFactory = clientFactory; } }
private void HttpClientFactoryTest() { var client = _clientFactory.CreateClient("这是专门用来链接博客园的");//必须和services.AddHttpClient()中指定的名称对应 var content = new StringContent($"SID={SID}&safeKey={111}"); content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"); var response = client.PostAsync("MyBlogUrl", content); }
这里咱们首先添加对IHttpClientFactory的依赖,它将由DI系统注入ClassInService。IHttpClientFactory容许咱们请求和接收HttpClient实例。
咱们使用HttpClientFactory建立客户端。在幕后,HttpClientFactory将为咱们建立一个新的HttpClient。可是等等,以前说过为每一个请求使用新的HttpClient是很糟糕。但此处的建立的httpclient是在他所管理的池子中,并不每一个请求都会是新的socket。
HttpClientFactory收集这些HttpClientHandler实例并管理它们的生命周期,以解决以前提到的一些问题。每次咱们要求HttpClient时,咱们都会获得一个新实例,它可能(或可能不)使用现有的HttpClientHandler。HttpClient自己并无问题。
一旦建立,由此建立的全部HttpClientHandler将被默认保持约2分钟。这意味着针对同一个CreateClient的任何新请求均可以共享处理程序,所以也能够共享链接。当HttpClient存在时,它的处理程序将保持可用状态,而且它将再次共享链接。
两分钟后,每一个HttpClientHandler都标记为已过时。过时状态只是标记它们,以便在建立任何新的HttpClient实例时再也不使用它们。可是,它们不会当即销毁,由于其余HttpClient实例可能正在使用它们。HttpClientFactory使用后台服务监视过时的处理程序,一旦它们再也不被引用,就能够正确释放它们,也容许它们的链接被关闭。
经过使用HttpClientfactory咱们不须要考虑如何管理HttpClient的生命周期或担忧遇到DNS问题。以上只是HttpClient小小的最佳使用推荐,还有其余高级用法,例如和Polly的结合使用。
参考:https://www.stevejgordon.co.uk/introduction-to-httpclientfactory-aspnetcore