引言:html
ASP.NET Core2.1 中出现了一个新的 HttpClientFactory 功能, 它有助于解决开发人员在使用 HttpClient 实例从其应用程序中访问外部 web 资源时可能遇到的一些常见问题。关于HttpClientFactory 到底解决了那些HttpClient的严重问题,下面是我罗列出来的(原文来自于:https://www.infoq.com/news/2016/09/HttpClient)web
(1)在处理HttpClient对象的时候不会当即关闭socket。数据库
(2)太多的实例影响性能安全
(3)单例的HttpClient或者共享HttpClient实例,不遵照DNS 生存时间 (TTL) 设置。(这个问题我也不太明白,具体怎么重现这个问题,我下去再研究研究。)服务器
HttpClientFactory这个小可爱,解决了上面的全部问题,他也是ASP.NET Core2.1最新特色之一,下面详细聊聊HttpClient存在的这些问题。网络
因为设计错误、bug 和文档不正确等因素, 致使在.Net中正确使用HttpClient 出奇的难。所以, 在生产环境中看起来正常工做的应用程序可能会在负载大的状况下产生性能和运行时故障的问题。socket
为了理解咱们为何遇到这种状况, 咱们首先要看另外一个面向链接的类: SqlConnection。 这个类实现了IDisposable接口,因此绝大多数开发人员都是这样写的,实例以下:async
using (var con = new SqlConnection(connectionString)) {
con.open();
//use the connection here
} //this closes the connection
tcp
虽然,这个例子在解释HttpClient存在的问题不是很到位,可是使用这种方式,来写上面的代码,是没错的。若是你尝试这把这种模式应用到实现了IDisposable接口的HttpClient,则会遇到一些很奇怪的问题。具体的说,它会打开比实际须要更多的socket,加剧了服务器的负载。此外使用using语句是不会关闭这些套接字的,相反,在应用程序中止使用它们时,会关闭几分钟。工具
Connection Pooling
回到 SqlConnection 示例中, 大多数面向链接的资源都是有链接池的。当您 "打开 " 数据库链接时, 它首先检查池中是否有可用的、未使用的链接。若是它找到一个, 将重用它, 而不是建立一个新的链接。
一样, 当您 "关闭 " SqlConnection 它只是将链接释放到连接池中。最终, 一个单独的进程可能会关闭长时间未使用的链接。
HttpClient 不这样作。当您处理它时, 它将启动关闭它所控制的套接字的过程。这意味着下次有请求时, 您必须通过一个全新的链接周期。若是您的网络有很高的延迟或您的链接是安全的, 则这会特别痛苦, 由于后者须要新一轮的 SSL/TLS 协商。
Closing a Socket Takes Four Minutes
如上所述,关闭套接字不是一个快速的过程。 当你“关闭”套接字时,你真正在作的是将它置于TIME_WAIT状态。 Windows将在此状态下保持链接240秒,以防万一剩余的数据包仍在传输中。
这使您更有可能耗尽可用套接字的数量,从而致使运行时错误,例如“没法链接到远程服务器.System.Net.Sockets.SocketException:每一个套接字地址只有一种用法(协议/ 网络地址/端口一般是容许的“。
下面咱们来实践一下,看看真相:
示例演示:(注意使用的是.Net Core 1.0)
using System; using System.Net.Http; using System.Threading.Tasks; namespace ConsoleApp2 { class Program { static async Task Main(string[] args) { Console.WriteLine("Starting connections"); for (int i = 0; i < 10; i++) { using (var client = new HttpClient()) { var result = await client.GetAsync("http://aspnetmonsters.com/");
Console.WriteLine(result.StatusCode);
}
}
Console.WriteLine("Connections done");
Console.ReadKey();
}
}
}
这将会打开10个请求,并以get的方式,去请求博客园,咱们只打印出状态码。
输出结果:
到这个地步,可能咱们就会很高兴,搞定!闪人!,真的搞定了吗?个人小可爱,下面咱们使用netstat 工具并查看运行它的机器上的套接字状态,咱们将看到:
看到没,个人应用程序已经执行完了,可是,仍然有不少连接在打开上面图示中的主机,它们都处于TIME_WAIT状态,这意味这咱们在应用程序这边已经把连接关闭了,可是咱们仍然在等待查看,是否有额外的数据包进来,使用这些连接。由于它们可能在网络的某个地方已经被延迟,下面咱们来看一张TCT/IP图,该图引自(https://www4.cs.fau.de/Projects/JX/Projects/TCP/tcpstate.html)
Windows将在此状态下保持链接240秒(由[HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Services \ Tcpip \ Parameters \ TcpTimedWaitDelay]设置)。 Windows能够快速打开新套接字的速度有限,所以若是您耗尽链接池,那么您可能会看到以下错误:
Unable to connect to the remote server
System.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted.
在谷歌搜索会给你一些关于减小链接超时的可怕建议。 事实上,当在服务器上运行着正确使用HttpClient或相似构造的应用程序时,减小超时可能会致使其余不利后果。 咱们须要了解“正确”意味着什么,并去修复底层的问题而不是去修补机器级的变量。
若是咱们共享一个HttpClient实例,那么咱们能够经过重用它们来减小套接字的浪费:
请注意,咱们只为整个应用程序共享了一个HttpClient实例。 仍然能够正常工做(实际上因为套接字重用而快一点)。 Netstat如今只显示:
好了,总结一下:在.Net Core 1.0以前的版本,使用的时候须要注意下面的两点:
(1)确保你的HttpClient
是 static
(2)不要丢弃或包装HttpClient 在一个using块中。
小弟我才疏学浅,有不对的地方能够指出来,共同探讨。其实,咱们一直使用using把它包起来,也是没错的,是由于HttpClient实现了IDisposable
,可是HttpClient就比较特殊,这不怪咱们,文档就是错误的。
HttpClientFactory 这个小可爱,就解决了上面的全部问题,她也是ASP.NET Core 2.1中最新特色之一,有了她咱们就不用关心如何建立HttpClient,又如何释放它。关于如何使用它,博客园中的有介绍的,我这里就再也不讲述了。
具体请参考:https://www.cnblogs.com/willick/p/9640589.html
到这里该系列文章的第一篇就讲完了,有不对的地方,还请大佬们能指出来,共同探讨,好了,但愿对你有所帮助,该系列文章,每周末更新,爱看不看,哈哈哈~~~~~~
参考文章:
(翻译)https://www.infoq.com/news/2016/09/HttpClient
(翻译)https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
做者:郭峥
出处:http://www.cnblogs.com/runningsmallguo/
本文版权归做者和博客园共有,欢迎转载,但未经做者赞成必须保留此段声明,且在文章页面明显位置给出原文连接。