.NetCore 2.1中的HttpClientFactory最佳实践
.NET Core 2.1中的HttpClientFactory最佳实践
ASP.NET Core 2.1中出现一个新的HttpClientFactory功能,
它有助于解决开发人员在使用HttpClient实例从其应用程序发出外部Web请求时可能遇到的一些常见问题。
介绍
在.NETCore平台的2.1新增了HttpClientFactory,虽然HttpClient这个类实现了disposable,但使用它的时候用声明using包装块的方式通常不是最好的选择。处理HttpClient,底层socket套接字不会立即释放。该HttpClient类是专为多个请求重复使用而创建的。需要不同的基地址,不同的HTTP标头和其他对请求个性化操作的场景时,需要手动管理多个HttpClient实例,为了简化HttpClient实例管理,.NET Core 2.1提供了一个新的HTTPClientFactory - 它可以创建,缓存和处理HttpClient实例。
什么是HttpClientFactory?
用ASP.NET团队的话说:“an opinionated factory for creating HttpClient instances”(一个用于创建HttpClient实例的最佳实践的工厂),并且是ASP.NET Core 2.1发布的新功能。根据大家以前使用HttpClient的经验,您可能遇到一些困扰的问题,有时甚至没有意识到您有问题(只是在并发并不大的场景没触发而已)。
第一个问题是当你在代码中创建太多的HttpClients时,这反过来会产生两个问题......
- 这是低效的,因为每个请求都有自己的远程服务器连接池。这意味着您需要为每个创建的客户端支付重新连接到该远程服务器的成本。
- 更大的问题是如果你创建了很多HttpClient并使用到他们,你可以遇到Socket耗尽,而你基本上已经太快地使用了过多的Socket。您可以同时打开多个Socket是有限制的。当您dispose销毁HttpClient时,它打开的连接在TIME_WAIT状态下保持打开状态最长240秒(如果来自远程服务器的任何数据包仍然通过)。
HttpClient实现了IDisposable,这通常会导致开发人员在使用IDisposable对象时遵循正常模式,在using块中创建它。这样可以确保一旦完成对象并且它已经超出范围,就可以正确销毁对象。
因此,最优的方法是重用HttpClient实例,以便也可以重用连接。HttpClient是一个可变对象,但只要你没有运行期改变它,它实际上是线程安全的并且可以共享。因此,一种常见的方法是将其注册为具有DI框架的单例模式,或者创建包含static静态实例的对象。
但是,这会产生新问题。以这种方式使用单个HttpClient将保持连接打开并且不遵守DNS生存时间(TTL)设置(总之就是同一个HttpClient实例只能有一个请求头,在被请求方发生更改时,由于是单例不能做个性化改变,否则导致其他请求失败)。现在连接将永远不会获得DNS更新,因此您正在与之通信的服务器将永远不会更新其地址。在某些情况下,这是完全有可能的,在以上这种情况下,您可以平衡许多主机,这些主机可能随着时间的推移而改变,或者可能使用Blue/Green 部署推出新服务。如果服务器消改变,则您的连接使用的IP可能不再响应您通过单个HttpClient发出的请求。
所以需要我们手动去管理每类服务器的HttpClient的实例来进行个性化请求头的构造和发起请求!
HttpClientFactory旨在帮助您开始解决这些问题,并提供了一种新的机制来创建在幕后为我们正确管理的HttpClient实例。它将为我们“做管理HttpClient的事”,我们可以专注于业务!虽然在参考HttpClient时提到了上述问题,但事实上问题的根源实际上发生在HttpClient上,HttpClient使用了HttpClientHandler。HttpClientFactory管理处理程序的生命周期,以便我们有一个可以重用的池,同时还可以(Rotating)轮换它们以使DNS不会过时。
使用HttpClient的昂贵部分实际上是创建HttpClientHandler和连接。以这种HttpClientFacotry方式汇集这些内容意味着我们可以更高效利用资源最节省地使用我们系统上的socket。当您使用HttpClientFactory请求HttpClient时,实际上每次都会获得一个新实例,这意味着我们不必担心会改变它的状态。此HttpClient可能(或可能不)使用池中的现有HttpClientHandler,从而使用现有打开的连接。
默认情况下,每个新创建的HttpClientHandler(派生自HttpMessageHandler)生命周期只有2分钟。通过services.AddHttpClient()创建HttpClientFactory实例时,可以根据每一个命名的Client客户机进行控制。达到生命周期后,处理程序将不会立即被释放掉,而是放入过期的池中。任何依赖于HttpClientFactory的处理程序链的客户端都可以继续使用它而没有任何问题。有一个后台作业检查过期的池,以查看处理程序的所有引用是否已在scope之外,此时可以将其释放掉。处理程序链过期后对新客户端的任何新请求都将获得新的处理程序链。
这种方法运行得相当不错,但.NET Core方面还有其他一些事情可能会进一步改善这种情况。.NET Core团队开发了一个新的ManagedHandler,它可以更正确地管理DNS,原则上可以保持更长时间,这意味着可以更有效地共享连接。这个新的处理程序还被设计为在不同的操作系统中更加一致地运行。在该工作完成之前,上面的处理程序池是一个合理的解决方法。
如何使用HttpClientFactory
我们将首先创建一个简单的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
.NetCore 2.1中的HttpClientFactory最佳实践的更多相关文章
- .NET Core 2.1中的HttpClientFactory最佳实践
ASP.NET Core 2.1中出现一个新的HttpClientFactory功能, 它有助于解决开发人员在使用HttpClient实例从其应用程序发出外部Web请求时可能遇到的一些常见问题. 介绍 ...
- NET中异常处理的最佳实践
NET中异常处理的最佳实践 本文翻译自CodeProject上的一篇文章,原文地址. 目录 介绍 做最坏的打算 提前检查 不要信任外部数据 可信任的设备:摄像头.鼠标以及键盘 “写操作”同样可能失效 ...
- XAML: 自定义控件中事件处理的最佳实践
在开发 XAML(WPF/UWP) 应用程序中,有时候,我们需要创建自定义控件 (Custom Control) 来满足实际需求.而在自定义控件中,我们一般会用到一些原生的控件(如 Button.Te ...
- Vue中CSS模块化最佳实践
Vue风格指南中介绍了单文件组件中的Style是必须要有作用域的,否则组件之间可能相互影响,造成难以调试. 在Vue Loader Scope CSS和Vue Loader CSS Modules两节 ...
- Window下使用Xshell连接VirtualBox中CentOS SSH最佳实践
网上已经有非常多讲怎样连接VMware的文章.可是针对一些可能遇到的细节没有讲全. 这里会有一个非常 实际的样例,附带全部软件的链接,保证成功. 最佳实践什么的都是骗人的. 1.安装VirtualBo ...
- Android中活动的最佳实践(如何很快的看懂别人的代码activity)
这种方法主要在你拿到别人的代码时候很多activity一时半会儿看不懂,用了这个方法以后就可以边实践操作就能够知道具体哪个activity是干什么用的 1.新建一个BaseActivity的类,让他继 ...
- .NET中异常处理的最佳实践(译)
本文翻译自CodeProject上的一篇文章,原文地址. 目录 介绍 做最坏的打算 提前检查 不要信任外部数据 可信任的设备:摄像头.鼠标以及键盘 “写操作”同样可能失效 安全编程 不要抛出“new ...
- .NET中异常处理的最佳实践(转)
原文出处: CodeProject 译文出处:周见智的博客 欢迎分享原创到伯乐头条 介绍 “我的软件程序从来都不会出错”.你们相信吗?我几乎可以肯定所有人都会大喊我是个骗子.“软件程序几乎不可 ...
- MySQL 中存储时间的最佳实践
平时开发中经常需要记录时间,比如用于记录某条记录的创建时间以及修改时间.在数据库中存储时间的方式有很多种,比如 MySQL 本身就提供了日期类型,比如 DATETIME,TIMESTAMEP 等,我们 ...
随机推荐
- 光盘yum源搭建
先修改基本的yum原源,使其不生效 cd /etc/yum.repos.d/ mv CentOS-Base.repo CentOS-Base.repo.bak 在修改媒介yum源使其生效 检验是否生效 ...
- MySQL各类SQL语句的加锁机制
官网参考:https://dev.mysql.com/doc/refman/5.6/en/innodb-locks-set.html MySQL把读操作分为两大类:锁定读和非锁定读(即locking ...
- SSM框架—环境搭建(MyEclipse+Tomcat+MAVEN+SVN)
1.JDK的安装 首先下载JDK,这个从sun公司官网可以下载,根据自己的系统选择64位还是32位,安装过程就是next一路到底.安装完成之后当然要配置环境变量了. 1.1新建变量名:JAVA_HOM ...
- Thinkphp框架中自定义修改success和error页面
Thinkphp框架中自定义修改success和error页面 Thinkphp框架的默认success和error太难看,可以自定义设置,步骤如下: (注意:TP原框架中的success跳转有问题, ...
- 【算法】LeetCode算法题-Longest Common Prefix
这是悦乐书的第146次更新,第148篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第5题(顺位题号是14),给定一个随机的字符串数组,查找这些字符串元素的公共前缀字符串, ...
- 我的BRF+自学教程(一):公式(formula)
Business Rule Framework Plus(业务规则框架,以下简称BRFplus或BRF+)是一个强大的工具, 它允许用户以直观的方式对规则建模,并在不同的应用程序中重用这些规则.通过它 ...
- 缓存数据库Memcache
为什么用缓存数据库 MySQL:将数据存储在磁盘上,数据写入读取相对较慢 Memcached:将数据存在内存中的数据库,数据读写都快,但是数据容易丢失 数据存储,数据仓库选择MySQL这种磁盘的数据库 ...
- 《程序员的自我修养》读书笔记——系统调用、API
系统调用 程序运行的时候,本身是没有权限访问多少系统资源的.系统资源有限,如果操作系统不进行控制,那么各个程序难免会产生冲突.线程操作系统都将可能产生冲突的系统资源保护起来,阻止程序直接访问. ...
- css3 object-fit详解
上传头像的时候遇到了头像变形的问题,最后通过object-fit: cover完美解决了.这个CSS属性可以达到最佳最完美的居中自动剪裁图片的功能. object-fit理解 CSS3 backgro ...
- 转://如何增加linux根目录的磁盘空间(基于LVM)?
问题引出: 在测试过程中替换so文件,报磁盘空间不足的错误. ▲问题分析: 由于当时系统部署架构的考虑,把软件和数据库部署在了同一台机器上,并且给了30G的磁盘空间.系统上占用磁盘空间的有2部分,一是 ...