.NET Core的HttpClient连接池管理

译者荐语:使用.NET Core的HttpClient连接池管理有哪些注意事项?本文给出了非常中肯的建议。

原文来自互联网,由长沙DotNET技术社区编译。如译文侵犯您的署名权或版权,请联系小编,小编将在24小时内删除。

本文来源[1]

史蒂夫·戈登(Steve Gordon)是Microsoft MVP,Pluralsight的作者,布莱顿(英国西南部城市)的高级开发人员和社区负责人。他的个人博客为:

www.stevejgordon.co.uk[2]

导读:.NET Core(从2.1开始)中的HttpClient执行连接池和这些连接的生命周期管理。这支持使用单个HttpClient实例,通过单例减少了套接字耗尽的机会,同时确保连接定期重新连接以反映DNS更改。

回顾HttpClient的历史

HttpClient最初是作为NuGet包开始的,该包可以选择包含在.NET Framework 4.0项目中。在.NET Framework 4.5中,它作为BCL(基本类库)的一部分在框中提供。它建立在预先存在的HttpWebRequest实现之上。在.NET Framework中,ServicePoint API可用于控制和管理HTTP连接,包括通过为端点配置ConnectionLeaseTimeout来设置连接寿命。

图片

.NET Core 1.0最初于2016年6月发布。与.NET Framework中可用的版本相比,此第一个版本的API接口要小得多,主要用于构建ASP.NET Core Web应用程序。由于.NET Core 1.0是HttpClient,因此提供了API。但是,不包括用于HttpWebRequest和ServicePoint的API。.NET Core 1.0中的HttpClient直接建立在使用非托管代码的OS平台API之上,Windows API使用WinHTTP,Linux和Mac使用LibCurl。

图片

到2016年8月,很快就注意到,重新使用HttpClient实例以防止套接字耗尽的建议有一个相当麻烦的副作用。Oren Novotny(译者注:.NET基金会执行董事,.NET团队的项目经理)揭开了一个长期存在的GitHub问题,题为“ Singleton HttpClient doesn’t respect DNS changes[3] ”(单例HttpClient不遵守DNS 更改)。在此问题中,人们认识到重新使用单个HttpClient实例将导致连接无限期保持打开状态,因此,DNS更改可能会导致请求失败或与过时的终结点通信。

在.NET Core 2.0中,添加了HttpWebRequest以支持.NET Standard 2.0。它位于HttpClient实现的顶层,这与.NET Framework 4.5+中的工作原理相反。还添加了ServicePoint,尽管它的许多API接口要么要么会抛出未实现的异常,要么根本就没有实现。

图片

自.NET CORE 2.1以来的变化

这种有问题的行为导致团队不同团队进行了两项工作。ASP.NET团队开始研究Microsoft.Extensions.Http包,该包的主要功能是IHttpClientFactory。这个针对HttpClient实例专用的工厂还包括基础HttpMessageHandler链的生命周期管理。如果您想了解有关此功能的更多信息,可以查看我的系列博客文章[4],我将在此介绍。

IHttpClientFactory功能是作为ASP.NET Core 2.1的一部分发布的,对于许多人来说,这是一个很好的折中方案,它解决了连接重用以及生命周期管理的问题。

在同一时间范围内,.NET团队正在研究自己的解决方案。该团队也在.NET Core 2.1中发布,在HttpClient的处理程序链的核心引入了一个新的SocketsHttpHandler。该处理程序直接建立在Socket API之上,并在托管代码中实现HTTP。这项工作的一部分包括连接池系统以及为这些连接设置最大生存期的能力。此功能将是本文其余部分的重点。

图片

但是在开始之前,我想指出,虽然默认情况下从.NET Core 2.1启用了SocketsHttpHandler,但实现仅限于HTTP / 1.1通信。那些需要HTTP / 2的用户必须禁用该功能并使用较旧的处理程序链,该处理程序链像以前一样依赖非托管代码,并且不包括连接池。

幸运的是,.NET Core 3.0中已消除了此限制,并且现在提供了HTTP/2支持。这应该使用基于适合所有对象的SocketsHttpHandler链的HttpClient。

什么是连接池?

SocketsHttpHandler为每个唯一端点建立连接池,您的应用程序通过HttpClient向该唯一端点发出出站HTTP请求。在对端点的第一个请求上,当不存在现有连接时,将建立一个新的HTTP连接并将其用于该请求。该请求完成后,连接将保持打开状态并返回到池中。

对同一端点的后续请求将尝试从池中找到可用的连接。如果没有可用的连接,并且尚未达到该端点的连接限制,则将建立新的连接。达到连接限制后,请求将保留在队列中,直到连接可以自由发送它们为止。

我一直在研究此实现的内部代码,并可能在以后的博客文章中对池的行为进行更深入的分析。

如何控制连接池

有三个主要设置可用于控制连接池的行为。

PooledConnectionLifetime,定义连接在池中保持活动状态的时间。此生存期到期后,将不再为将来的请求而合并或发出连接。

PooledConnectionIdleTimeout,定义闲置连接在未使用时在池中保留的时间。一旦此生存期到期,空闲连接将被清除并从池中删除。

MaxConnectionsPerServer,定义每个端点将建立的最大出站连接数。每个端点的连接分别池化。例如,如果最大连接数为2,则您的应用程序将请求发送到两个www.github.com[5]和www.google.com[6],总共可能最多有4个打开的连接。

默认情况下,从.NET Core 2.1开始,更高级别的HttpClientHandler将SocketsHttpHandler用作内部处理程序。没有任何自定义配置,将应用连接池的默认设置。

PooledConnectionLifetime默认是无限的,因此,虽然经常使用的请求,连接可能会无限期地保持打开状态。该PooledConnectionIdleTimeout默认为2分钟,如果在连接池中长时间未使用将被清理。MaxConnectionsPerServer默认为int.MaxValue,因此连接基本上不受限制。

如果希望控制这些值中的任何一个,则可以手动创建SocketsHttpHandler实例,并根据需要进行配置。

var socketsHandler = new SocketsHttpHandler{PooledConnectionLifetime = TimeSpan.FromMinutes(10),PooledConnectionIdleTimeout = TimeSpan.FromMinutes(5),MaxConnectionsPerServer = 10};var client = new HttpClient(socketsHandler);

在前面的示例中,对SocketsHttpHandler进行了配置,以使连接将最多在10分钟后停止重新发出并关闭。如果闲置5分钟,则连接将在池的清理过程中被更早地删除。我们还将最大连接数(每个端点)限制为十个。如果我们需要并行发出更多出站请求,则某些请求可能会排队等待,直到10个池中的连接可用为止。要应用处理程序,它将被传递到HttpClient的构造函数中。

测试连接寿命

以这个示例程序为例:

using System;using System.Net.Http;using System.Threading.Tasks;namespace HttpConnectionPoolingSamples{class Program{static async Task Main(string[] args){var ips = await Dns.GetHostAddressesAsync("www.google.com");foreach (var ipAddress in ips){Console.WriteLine(ipAddress.MapToIPv4().ToString());}var socketsHandler = new SocketsHttpHandler{PooledConnectionLifetime = TimeSpan.FromMinutes(10),PooledConnectionIdleTimeout = TimeSpan.FromMinutes(5),MaxConnectionsPerServer = 10};var client = new HttpClient(socketsHandler);for (var i = 0; i < 5; i++){_ = await client.GetAsync("https://www.google.com");await Task.Delay(TimeSpan.FromSeconds(2));}Console.WriteLine("Press a key to exit...");Console.ReadKey();}}}

使用我们刚刚讨论的设置,此代码依次向同一端点发出5个请求。在每个请求之间,它会暂停两秒钟。该代码还输出从DNS检索到的Google服务器的IPv4地址。我们可以使用此IP地址来查看通过PowerShell中发出的netstat命令对其打开的连接:

netstat -ano | findstr 216.58.211

在我的例子中,此命令的输出为:

TCP   192.168.1.139:53040   216.58.211.164:443   ESTABLISHED   20372

我们可以看到,在这种情况下,到远程端点的连接只有1个。在每个请求之后,该连接将返回到池中,因此在发出下一个请求时可以重新使用。如果我们更改连接的生存期,以使它们在1秒后过期,那么我们可以测试这对行为的影响:

using System;using System.Net;using System.Net.Http;using System.Threading.Tasks;namespace HttpConnectionPoolingSamples{class Program{static async Task Main(string[] args){var ips = await Dns.GetHostAddressesAsync("www.google.com");foreach (var ipAddress in ips){Console.WriteLine(ipAddress.MapToIPv4().ToString());}var socketsHandler = new SocketsHttpHandler{PooledConnectionLifetime = TimeSpan.FromSeconds(1),PooledConnectionIdleTimeout = TimeSpan.FromSeconds(1),MaxConnectionsPerServer = 10};var client = new HttpClient(socketsHandler);for (var i = 0; i < 5; i++){_ = await client.GetAsync("https://www.google.com");await Task.Delay(TimeSpan.FromSeconds(2));}Console.WriteLine("Press a key to exit...");Console.ReadKey();}}}
TCP   192.168.1.139:53115   216.58.211.164:443   TIME_WAIT     0
TCP   192.168.1.139:53116   216.58.211.164:443   TIME_WAIT     0
TCP   192.168.1.139:53118   216.58.211.164:443   TIME_WAIT     0
TCP   192.168.1.139:53120   216.58.211.164:443   TIME_WAIT     0
TCP   192.168.1.139:53121   216.58.211.164:443   ESTABLISHED   25948

在这种情况下,我们可以看到使用了五个连接。其中的前四个在1秒后从池中删除,因此无法在下一个请求中重复使用。结果,每个请求都打开了一个新连接。现在,原始连接处于TIME_WAIT状态,并且操作系统无法将其重新用于新的出站连接。最终连接显示为ESTABLISHED,因为我在它过期之前就抓住了它。

测试最大连接数

对于下一个测试用例,我们将使用以下程序:

using System;using System.Diagnostics;using System.Linq;using System.Net;using System.Net.Http;using System.Threading.Tasks;namespace HttpConnectionPoolingSamples{class Program{static async Task Main(string[] args){var ips = await Dns.GetHostAddressesAsync("www.google.com");foreach (var ipAddress in ips){Console.WriteLine(ipAddress.MapToIPv4().ToString());}var socketsHandler = new SocketsHttpHandler{PooledConnectionLifetime = TimeSpan.FromSeconds(60),PooledConnectionIdleTimeout = TimeSpan.FromMinutes(20),MaxConnectionsPerServer = 2};var client = new HttpClient(socketsHandler);var sw = Stopwatch.StartNew();var tasks = Enumerable.Range(0, 200).Select(i => client.GetAsync("https://www.google.com"));await Task.WhenAll(tasks);sw.Stop();Console.WriteLine($"{sw.ElapsedMilliseconds}ms taken for 200 requests");Console.WriteLine("Press a key to exit...");Console.ReadKey();}}}

该代码将MaxConnectionsPerServer限制为2。然后启动200个任务,每个任务都向同一端点发出HTTP请求。这些任务将同时运行。所有请求竞争所花费的时间将写入控制台。在我的机器上运行此命令后,输出为:

 8013ms taken for 200 requests

如果使用netstat查看连接,则根据定义的限制,我们可以看到两个已建立的连接。已建立

TCP   192.168.1.139:52780   216.58.204.36:443   ESTABLISHED   16076
TCP   192.168.1.139:52780   216.58.204.36:443   ESTABLISHED   16076

如果我们调整此代码以允许MaxConnectionsPerServer = 10,则可以重新运行该应用程序。这次所花费的时间减少了大约4倍。

2123ms taken for 200 requests

当我们查看连接时,我们可以看到确实建立了十个连接。

TCP   192.168.1.139:52798   216.58.204.36:443   ESTABLISHED   30856
TCP   192.168.1.139:52799   216.58.204.36:443   ESTABLISHED   30856
TCP   192.168.1.139:52800   216.58.204.36:443   ESTABLISHED   30856
TCP   192.168.1.139:52801   216.58.204.36:443   ESTABLISHED   30856
TCP   192.168.1.139:52802   216.58.204.36:443   ESTABLISHED   30856
TCP   192.168.1.139:52803   216.58.204.36:443   ESTABLISHED   30856
TCP   192.168.1.139:52804   216.58.204.36:443   ESTABLISHED   30856
TCP   192.168.1.139:52805   216.58.204.36:443   ESTABLISHED   30856
TCP   192.168.1.139:52806   216.58.204.36:443   ESTABLISHED   30856
TCP   192.168.1.139:52807   216.58.204.36:443   ESTABLISHED   30856

结果,提高了吞吐量。我们允许更多的出站连接,因此可以更快地处理请求队列,并通过额外的连接并行发出更多请求。

我们还需要IHttpClientFactory吗?

这是一个非常合乎逻辑的问题,可以作为这篇帖子的结论。IHttpClientFactory的功能之一是HttpMessageHandler链的生命周期管理,因此也是基础连接的生命周期管理。有了HttpClient和SocketsHttpHandler可以达到相同效果的知识,我们是否需要使用IHttpClientFactory?

我的观点是,IHttpClientFactory除了帮助管理连接生存期外还有其他好处,并且在发出出站HTTP请求时仍然可以增加价值。它提供了一种很好的模式,可以使用命名或类型化的客户端方法[7]为HttpClient实例定义逻辑配置。类型化的客户端是我个人的最爱。

这些逻辑客户端的流畅配置方法还使定制的DelegatingHandlers[8]与客户端的使用非常简单明了。这包括ASP.NET团队对该方法的扩展,以便与Polly集成,[9]以便轻松地对出站请求应用弹性和瞬时故障处理。

即使没有生命周期管理,我也希望在将来的一段时间内将工厂用于我的应用程序。根据我在网上看到的讨论,很有可能在将来的版本中,寿命管理功能将从IHttpClientFactory中弃用和/或删除,因为它解决的问题不再适用。

摘要

在本文中,我们看到自从.NET Core 2.1发布以来,使用默认的SocketsHttpHandler实现时,将维护连接池。使用池的设置,我们可以控制连接的生存期并限制每个端点可能创建的出站连接的数量。

我们还讨论了IHttpClientFactory不仅具有连接生存期管理的优点和功能,因此仍然是一个有价值的工具。

References

[1] 本文来源: https://www.stevejgordon.co.uk/httpclient-connection-pooling-in-dotnet-core
[2] www.stevejgordon.co.uk: http://www.stevejgordon.co.uk
[3] Singleton HttpClient doesn’t respect DNS changes: https://github.com/dotnet/corefx/issues/11224
[4] 系列博客文章: https://www.stevejgordon.co.uk/introduction-to-httpclientfactory-aspnetcore
[5] www.github.com: http://www.github.com/
[6] www.google.com: http://www.google.com/
[7] 命名或类型化的客户端方法: https://www.stevejgordon.co.uk/httpclientfactory-named-typed-clients-aspnetcore
[8] 定制的DelegatingHandlers: https://www.stevejgordon.co.uk/httpclientfactory-aspnetcore-outgoing-request-middleware-pipeline-delegatinghandlers
[9] 与Polly集成,: https://www.stevejgordon.co.uk/httpclientfactory-using-polly-for-transient-fault-handling

【DotNET技术圈】致力于打造硬核、高质量的.NET技术公众号,欢迎大家投稿。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/311356.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[蓝桥杯][2014年第五届真题]兰顿蚂蚁-模拟

题目描述 兰顿蚂蚁&#xff0c;是于1986年&#xff0c;由克里斯兰顿提出来的&#xff0c;属于细胞自动机的一种。 平面上的正方形格子被填上黑色或白色。在其中一格正方形内有一只“蚂蚁”。 蚂蚁的头部朝向为&#xff1a;上下左右其中一方。 蚂蚁的移动规则十分简单&#…

.NET Core开发实战(第17课:为选项数据添加验证:避免错误配置的应用接收用户流量)--学习笔记...

17 | 为选项数据添加验证&#xff1a;避免错误配置的应用接收用户流量三种验证方法1、直接注册验证函数2、实现 IValidateOptions3、使用 Microsoft.Extensions.Options.DataAnnotations延用上一节代码需要添加验证的时候不能用 Configure&#xff0c;而用 AddOptions 方法//se…

分支程序与循环程序设计-汇编实验二

DATA SEGMENT ;定义数据段BUF DB -1, 20, 3, 30, -5, 15, 100, -54, 0, 4, 78, 99DB -12, 32, 3, 23, -7, 24, 60,-51 ;定义比较大小的数据DATA ENDS ;数据段结束ESEG SEGMENT ;定义附加段RES1 DB 0;定义结果存放区RES2 DB 0RES3 DB 0 ESEG ENDS ;附加段结束CODE SEGMENT ;定义…

[蓝桥杯][2013年第四届真题]剪格子-dfs

题目描述 历届试题 剪格子 时间限制&#xff1a;1.0s 内存限制&#xff1a;256.0MB 问题描述 如下图所示&#xff0c;3 x 3 的格子中填写了一些整数。 我们沿着图中的星号线剪开&#xff0c;得到两个部分&#xff0c;每个部分的数字和都是60。 本题的要求就是请你编程判定&a…

使用有序GUID:提升其在各数据库中作为主键时的性能

原文出处&#xff1a;https://www.codeproject.com/articles/388157/guids-as-fast-primary-keys-under-multiple-database &#xff0c;避免今后忘记了再去阅读原英文。【】是感觉理解有问题的地方正确的使用有序GUID在大部分数据库中可以获得和 整型作为主键 时相媲美的性能。…

串操作指令及其应用程序的设计与调试运行——汇编实验四 用8086汇编完成下题 编制一程序,从键盘输入两个长度不同的字符串,设字符串长度小于25个字符。要求在屏幕上以右边对齐的形式显示出

文章目录实验目的和内容实验要求和步骤实现右对齐以下为源码&#xff1a;方法一方法二方法三测试一——字符串的输入输出测试二——改进测试三——改进测试四——改进实验目的和内容 实验要求和步骤 实现右对齐 以下为源码&#xff1a; 方法一 DATA SEGMENTNUM EQU 25BUF1 D…

selenium.common.exceptions.WebDriverException: Message: ‘chromedriver‘ executable needs to bein PATH

使用Selenium模拟浏览器访问淘宝首页&#xff0c;出现报警 from selenium import webdriver import timebrowser webdriver.Chrome() browser.get(https://www.taobao.com) time.sleep(2) print(browser.page_source)selenium.common.exceptions.WebDriverException: Message…

【朝夕技术专刊】Core3.1WebApi_Filter详解

欢迎大家阅读《朝夕Net社区技术专刊》第4期我们致力于.NetCore的推广和落地&#xff0c;为更好的帮助大家学习&#xff0c;方便分享干货&#xff0c;特创此刊&#xff01;很高兴你能成为忠实读者&#xff0c;文末福利不要错过哦&#xff01;01PARTCoreWebApi五大Filter1. Autho…

统计学习方法 pdf_机器学习基础教材-《统计学习与数据分析介绍》免费pdf分享...

本书介绍本入门级统计教科书主要讲解发展和培养统计思维所需的基本概念和工具。它提供了描述性&#xff0c;归纳性和探索性的统计方法&#xff0c;并指导读者完成定量数据分析的过程。在实验科学和跨学科研究中&#xff0c;数据分析已成为任何科学研究的组成部分。诸如判断数据…

selenium.common.exceptions.SessionNotCreatedException: Message: session not created全套解决方案

chromedriver版本不匹配 报错&#xff1a; selenium.common.exceptions.SessionNotCreatedException: Message: session not created selenium.common.exceptions.WebDriverException: Message: session not created: 原因&#xff1a; Chrome版本和ChromeDriver版本不一致…

.NET Core开发实战(第18课:日志框架:聊聊记日志的最佳姿势)--学习笔记(上)...

18 | 日志框架&#xff1a;聊聊记日志的最佳姿势源码链接&#xff1a;https://github.com/witskeeper/geektime/tree/master/samples/LoggingSimpleDemo日志框架必要的包&#xff1a;1、Microsoft.Extensions.Logging2、Microsoft.Extensions.Logging.Console3、Microsoft.Exte…

Istio 1.5 发布——拥抱变化,爱上单体

北京时间 2020 年 3 月 6 日凌晨&#xff0c;我们期待已久的 Istio 1.5 发布了&#xff0c;发布公告见 https://istio.io/news/releases/1.5.x/announcing-1.5/。由 ServiceMesher 社区组织翻译的 Istio 官方文档同时发布&#xff0c;见 https://istio.io/zh。Istio 1.5 是一个…

hbuilderx怎么添加断点_【高考语文题库】高考一直提分提不上去该怎么办?同一卷高考押题语文答案,助你再提30分...

大树从来不是在温室里长成的&#xff0c;而是在风霜雪雨的洗礼中参天的……古语云&#xff1a;“工欲善其事&#xff0c;必先利其器。”俗话说&#xff1a;“磨刀不误砍材工。”用到学习上就是学习必须讲究学习方法&#xff0c;有了适合自己的有效学习方法必定会事半功倍。一个…

业务模块化打造单体和分布式部署同步支持方案

我在2019年中国.NET开发者峰会上为大家分享了我们的微服务电商安全工程实践&#xff0c;那次会议分享的高清录播已经上传到我的腾讯课堂&#xff0c;大家可以通过底部的小程序打开直接观看&#xff08;复习&#xff09;。在大会上跟大家提到&#xff0c;我们当时只有4个人的创业…

Asp.Net Core EndPoint 终结点路由工作原理解读

Asp.Net Core EndPoint 终点路由工作原理解读一、背景在本打算写一篇关于Identityserver4 的文章时候&#xff0c;却发现自己对EndPoint -终结点路由还不是很了解&#xff0c;故暂时先放弃了IdentityServer4 的研究和编写&#xff1b;所以才产生了今天这篇关于EndPoint (终结点…

[蓝桥杯][算法提高VIP]夺宝奇兵-dp

题目描述 在一座山上,有很多很多珠宝,它们散落在山底通往山顶的每条道路上,不同道路上的珠宝的数目也各不相同.下图为一张藏宝地图: 7 3 8 8 1 0 2 7 4 4 4 5 2 6 5 ”夺宝奇兵”从山下出发,到达山顶,如何选路才能得到最多的珠宝呢?在上图所示例子中,按照5-> 7-> 8-&g…

迁移到其他机器_有赞大数据离线集群迁移实战

‍‍点击关注“有赞coder”获取更多技术干货哦&#xff5e;作者&#xff1a;郭理想 & 任海潮部门&#xff1a;数据中台一、背景有赞是一家商家服务公司&#xff0c;向商家提供强大的基于社交网络的&#xff0c;全渠道经营的 SaaS 系统和一体化新零售解决方案。随着近年来社…

C# 客户端内存优化分析

背景概述C# 开发客户端系统的时候&#xff0c;.net 框架本身就比较消耗内存资源,特别是xp 这种老爷机内存配置不是很高的电脑上运行,所以就需要进行内存上的优化&#xff0c;才能流畅的在哪些低端电脑上运行. 想要对C# 开发的客户端内存优化需要了解以下几个概念。虚拟内存这里…

xshell1分钟就会自动断_手术室自动门不能正常控制开关门维修案例

手术室自动门维修案例遵义市第五人民医院手术室的手术门。用户反映&#xff1a;不能正常控制开关门。一、原因分析&#xff1a;1.红外线安全传感器故障2.控制器故障3.直流电机故障4. 红外感应开关故障5.红外感应探头故障6.电源故障图1图2图3图4图5图6二、维修过程&#xff1a;1…

.NET Core开发实战(第18课:日志框架:聊聊记日志的最佳姿势)--学习笔记(下)...

18 | 日志框架&#xff1a;聊聊记日志的最佳姿势除了使用 CreateLogger 指定 logger 的名称&#xff0c;实际上还可以借助容器来构造 logger&#xff0c;通常情况下我们会定义自己的类namespace LoggingSimpleDemo {public class OrderService{ILogger<OrderService> _lo…