ASP.NET Core 2.1 中的 HttpClientFactory (Part 1) - HttpClientFactory介绍

原文:https://www.stevejgordon.co.uk/introduction-to-httpclientfactory-aspnetcore  
发表于:2018年1月html

ASP.NET Core 2.1中将出现一个新的HttpClientFactory功能,它有助于解决开发人员在使用HttpClient实例时可能遇到的一些问题。git

介绍

我从2017年11月中旬开始准备写这篇文章,当时我第一次注意到有一个新的 HttpClientFactory 版本库 出如今GitHub上。我对它的出现感到好奇,而且想知道 ASP.NET 团队在作什么,因此我深刻研究了当时存储库中的代码。从那之后我一直留意这个问题,关注代码更新、问题反馈和社区讨论,看着开发团队不断完善其功能。github

最近,该功能开始被更多的讨论,而且由Damian Edwards和David Fowler在NDC伦敦举行的一次演讲中说起。事实上,在撰写此介绍的那一天,它已经在Jeff Fritz的直播节目ASP.NET Community Standup上展现。Ryan Nowak是该功能的主要开发人员之一,他认为功能已足够稳定,能够向你们展现了。api

注意:这篇文章是在.NET Core 2.1的官方预览版以前使用ASP.NET Core 2.1和.NET Core SDK的每晚构建版本编写的。所以,根据从这些预览中收到的反馈,在公开预览以前和期间(但愿咱们将在下个月内得到这些内容)以及2.1的最终发布以前,可能会发生变化。安全

什么是 HttpClientFactory?

用ASP.NET团队的话说,它是“一个用于建立HttpClient实例的自觉得是的工厂”,而且是ASP.NET Core 2.1发布的新功能。根据您过去使用HttpClient的经验,您可能遇到过一些陷阱,或者可能没有意识到存在问题。服务器

第一个问题是当你在代码中建立太多的HttpClients时,这会带来两个负面问题:app

  1. 效率不高,由于每链接都有本身的远程服务器链接池。这意味着您须要为建立的每一个客户端从新链接到该远程服务器支付额外开销。
  2. 可能遇到的更大问题是,若是你在短期内建立了大量客户端,可能会遇到Socket耗尽问题。在必定时间内可使用的Socket是有限制的。当你使用HttpClient打开链接以后,它会保持最长240秒的TIME_WAIT状态(这期间来自远程服务器的任何数据包仍然经过)。

HttpClient实现了IDisposable,一般开发人员在使用IDisposable对象时会在using块中建立它,这样能够确保对象在使用完后被释放掉。若是你想阅读更多这方面的信息,ASP.NET Monsters在他们的文章“你正在错误的使用HttpClient,它使你的软件失去稳定性”中有很好的叙述。框架

一般,首选方法是重用HttpClient实例,以即可以重用链接。 HttpClient是一个可变对象(mutable object),但只要你没有改变它,它其实是线程安全的而且能够共享。所以,常见的方法是经过DI框架注册为单例,或者为其建立一个容器成为静态实例。socket

可是,这会产生新问题。这种方式并不遵照DNS生存时间(TTL)设置,链接将永远不会得到DNS更新,您与之通讯的服务器永远不会更新地址。async

在某些状况下,有可能使用多个主机(Hosts)作负载均,随着时间的推移,一些主机会消失,一些主机新加入进来。若是主机消失,您的单例HttpClient链接的IP地址则不会响应您的请求。您能够在“单例HttpClient?必须当心使用以及如何解决”和“单例HttpClient不听从DNS更新”阅读更多此类问题的信息。

HttpClientFactory被设计用来解决这些问题,并提供一种新的后台机制,来管理和建立HttpClient实例。它会为咱们作“该作的事情”,以便咱们能够专一于其它事情。虽然,上面问题都指向HttpClient,但实际上问题的根源是在HttpClient使用的HttpClientHandler上。HttpClientFactory 用来管理Handlers的生命周期,以便咱们能够重用池(pool),同时保证DNS不会过时。

使用HttpClient消耗最大的部分其实是建立HttpClientHandler和链接(Connection)。把它们放到池(pool)中是为了在系统中更加高效的使用他们。当咱们是使用HttpClientFactory请求一个HttpClient时,实际上每次都会获得一个新的实例,这意味这咱们不用担忧会改变(mutating)它的状态。HttpClient可能使用(也可能不使用)池(pool)中已有的HttpClientHandler来保持链接。

默认状况下,每一个新的HttpClientHandler(派生自HttpMessageHandler)将以2分钟的生命周期建立。在建立它的处理程序链(handler chain)时,能够在每一个命名的客户端上控制它。达到生命周期后,处理程序(handler)将不会当即被释放,而是放入过时的池中。任何基于原始处理程序链(original handler chain)的客户端均可以继续使用它。有一个后台做业检查过时的池,以查看处理程序的全部引用是否已超出范围,而后能够将其处理掉。处理程序链(handler chain)过时后对新客户端的任何新请求都将得到新的处理程序链。

这种方法可以很好的工做,但.NET Core还会更进一步。.NET Core团队正在开发一个新的ManagedHandler,它能够更好地管理DNS,原则上能够保持更长时间,这意味着能够更有效地共享链接。这个新的处理程序(handler)也被设计为在不一样的操做系统中更一致地运行。在该工做完成以前(可能在2.1时间范围内),上面的处理程序池是一个合理的解决方法。

如何使用HttpClientFactory

重要说明:下面的功能和代码示例须要SDK每晚构建版本以及.NET Core和ASP.NET Core库,我不会介绍如何设置和使用它们。仅为展现该功能如何工做,以便您能够在2.1正式发布时考虑是否使用它们。除非您今天迫切须要尝试这一点,不然我建议您等到2.1预览发布,但愿在下个月左右。

本节中,我将主要介绍HttpClientFactory的最基本用法。咱们将建立一个简单的WebAPI项目,而后编辑csproj文件以将其升级为使用新的.NET Core和ASP.NET Core 2.1。首先,咱们须要将其设置为基于netcoreapp2.1(还没有在官方预览中),而后包含咱们须要的两个包,咱们的项目文件以下所示:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp2.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.All" Version="2.1.0-preview1-28124" />
    <PackageReference Include="Microsoft.Extensions.Http" Version="2.1.0-preview1-28124" />
  </ItemGroup>
  
</Project>

接下来,咱们须要在Startup.cs注册服务。 HttpClientFactory有多种ServiceCollection扩展方式。咱们使用其中的一种:

services.AddHttpClient();

这会注册一些必需的服务,其中一个将是IHttpClientFactory的实现。接下来,咱们更新ValuesController以使用此功能:

[Route("api/[controller]")]
public class ValuesController : Controller
{
    private readonly IHttpClientFactory _httpClientFactory;

    public ValuesController(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    [HttpGet]
    public async Task<ActionResult> Get()
    {
        var client = _httpClientFactory.CreateClient();
        var result = await client.GetStringAsync("http://www.google.com");
        return Ok(result);
    }
}

这里咱们首先添加对IHttpClientFactory的依赖,它将由DI系统注入咱们的控制器。 IHttpClientFactory容许咱们请求和接收HttpClient实例。

在咱们的Get操做中,咱们使用HttpClientFactory建立客户端。在其内部,HttpClientFactory将为咱们建立一个新的HttpClient。可是,以前我不是说过为每一个请求建立新的HttpClient是很糟糕的吗?但实际上这有点误导。HttpClient自己并非真正的问题,而是用于实现HTTP调用的HttpClientHandler,这才是实际问题。HttpClientHandler用来打开与外部服务的链接,这些链接将保持打开并阻止sockets,即便主HttpClient被释放以后也是如此。

HttpClientFactory聚集这些HttpClientHandler实例并管理它们的生命周期,以解决我以前提到的一些问题。每次咱们请求HttpClient时,咱们都会获得一个新实例,它可能(或可能不)使用现有的HttpClientHandler。HttpClient自己并不过重,因此这不要紧。

一旦建立,HttpClientHandlers就会被放置到池(pool)中,默认状况下会保持约2分钟。这意味着任何一个新的CreateClient请求均可以共享一个处理程序,所以也能够共享链接。当HttpClient存在时,它的处理程序将保持可用,而且共享链接。

两分钟后,每一个HttpClientHandler都标记为已过时。过时状态只是标记,以便在建立任何新的HttpClient实例时再也不使用它们。可是,它们不会当即处理,由于其余HttpClient实例可能正在使用它们。 HttpClientFactory使用后台服务来监视过时的处理程序,一旦它们再也不被引用,就能够正确处理它们,也容许它们关闭链接。

池(pooling)功能有助于下降socket耗尽的风险,其刷新机制能够处理过长生命周期的HttpClientHandlers实例和挂起的链接,从而解决DNS更新问题。这是一个合理的折衷方案。

总结

本文就介绍到这里。在之后的文章中,我将深刻探讨一些HttpClientFactory的高级方法,由于有一些很好的功能值得展现。看看如何经过配置建立命名的HttpClient实例,以及建立本身的类型化客户端。这是该功能真正的亮点。但愿您已经了解在这个基本示例中,它是如何以最正确和有效的方式处理HTTP调用来知足咱们的用例。咱们不须要考虑如何管理客户端的生命周期或担忧遇到DNS问题。我但愿在ASP.NET Core 2.1正式发布后,这些功能可以应用到生产环境中去。

相关文章
相关标签/搜索