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

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

介绍

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

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

注意:这篇文章是在.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时,这会带来两个负面问题:

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

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

通常,首选方法是重用HttpClient实例,以便可以重用连接。 HttpClient是一个可变对象(mutable object),但只要你没有改变它,它实际上是线程安全的并且可以共享。因此,常见的方法是通过DI框架注册为单例,或者为其创建一个容器成为静态实例。

但是,这会产生新问题。这种方式并不遵守DNS生存时间(TTL)设置,连接将永远不会获得DNS更新,您与之通信的服务器永远不会更新地址。

在某些情况下,有可能使用多个主机(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.</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正式发布后,这些功能能够应用到生产环境中去。

ASP.NET Core 2.1 中的 HttpClientFactory (Part 1) HttpClientFactory介绍的更多相关文章

  1. 在ASP.NET Core 1.0中如何发送邮件

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:目前.NET Core 1.0中并没有提供SMTP相关的类库,那么要如何从ASP.NE ...

  2. ASP.NET Core 2.1 中的 HttpClientFactory (Part 3) 使用Handler实现传出请求中间件

    原文:https://www.stevejgordon.co.uk/httpclientfactory-aspnetcore-outgoing-request-middleware-pipeline- ...

  3. ASP.NET Core 2.1 中的 HttpClientFactory (Part 4) 整合Polly实现瞬时故障处理

    原文:https://www.stevejgordon.co.uk/httpclientfactory-using-polly-for-transient-fault-handling发表于:2018 ...

  4. 在 ASP.NET Core Web API中使用 Polly 构建弹性容错的微服务

    在 ASP.NET Core Web API中使用 Polly 构建弹性容错的微服务 https://procodeguide.com/programming/polly-in-aspnet-core ...

  5. ASP.NET Core HTTP 管道中的那些事儿

    前言 马上2016年就要过去了,时间可是真快啊. 上次写完 Identity 系列之后,反响还不错,所以本来打算写一个 ASP.NET Core 中间件系列的,但是中间遇到了很多事情.首先是 NPOI ...

  6. ASP.NET Core 1.0 中的依赖项管理

    var appInsights=window.appInsights||function(config){ function r(config){t[config]=function(){var i= ...

  7. ASP.NET Core 1.0 中使用 Swagger 生成文档

    github:https://github.com/domaindrivendev/Ahoy 之前文章有介绍在ASP.NET WebAPI 中使用Swagger生成文档,ASP.NET Core 1. ...

  8. 用ASP.NET Core 1.0中实现邮件发送功能

    准备将一些项目迁移到 asp.net core 先从封装类库入手,在遇到邮件发送类时发现在 asp.net core 1.0中并示提供SMTP相关类库,于是网上一搜发现了MailKit 好东西一定要试 ...

  9. 在ASP.NET Core Web API中为RESTful服务增加对HAL的支持

    HAL(Hypertext Application Language,超文本应用语言)是一种RESTful API的数据格式风格,为RESTful API的设计提供了接口规范,同时也降低了客户端与服务 ...

随机推荐

  1. 为什么static成员必须在类外初始化,而不能在类的头文件中初始化

    为什么static成员必须在类外初始化 为什么静态成员不能在类内初始化 在C++中,类的静态成员(static member)必须在类内声明,在类外初始化,像下面这样.   class A { pri ...

  2. Android平台云端打包证书使用说明

    原贴:https://ask.dcloud.net.cn/article/35985 Android平台云端打包证书使用说明 分类:HBuilderX 证书 Android 签名证书是一个应用的所有者 ...

  3. Linux记录-mysql服务管理shell实现

    #!/bin/bash menu() { echo "---欢迎使用mysql管理服务程序---" echo "# 1.启动服务" echo "# 2 ...

  4. Linux下手动查杀木马

    (1).模拟木马程序病原体并让其自动运行 黑客让脚本自动执行的3种方法:1.计划任务:2.开机启动:3.系统命令被人替换,定一个触发事件. 1)生成木马程序病原体 [root@youxi1 ~]# v ...

  5. e.target 和 e.currentTarget

    们可以得出: e.currentTarget指的是注册了事件监听器的对象,而e.target指的是该对象里的子对象,也是触发这个事件的对象!这么说应该明白 了吧?

  6. CSS控制元素背景透明度总结

    方法一:CSS3的background rgba filter:progid:DXImageTransform.Microsoft.gradient(enabled='true',startColor ...

  7. PHPStudy后门事件分析

    PHP环境集成程序包phpStudy被公告疑似遭遇供应链攻击,程序包自带PHP的php_xmlrpc.dll模块隐藏有后门.经过分析除了有反向连接木马之外,还可以正向执行任意php代码. 影响版本 P ...

  8. Redis概述与基本操作

    redis教程 概述 redis是一种nosql数据库,他的数据是保存在内存中,同时redis可以定时把内存数据同步到磁盘,即可以将数据持久化,并且他比memcached支持更多的数据结构(strin ...

  9. Linux习题1

    1. GUN的含义是:     GNU's Not UNIX    . 2. Linux一般有3个主要部分:内核.命令解释层.实用工具. 3.POSIX是可携式操作系统接口的缩写,重点在规范核心与应用 ...

  10. eclipse设置格式化tab为4个空格和idea一样