HttpClient 和套接字耗尽 - 澄清?
Posted
技术标签:
【中文标题】HttpClient 和套接字耗尽 - 澄清?【英文标题】:HttpClient and socket exhaustion - clarification? 【发布时间】:2019-10-17 08:05:25 【问题描述】:This article 表示我们应该使用静态的HttpClient
来重用套接字。
但是那里的第一条评论说存在DNS更改识别问题,解决方案在另一篇文章here:
第二条建议:
var client = new HttpClient();
client.DefaultRequestHeaders.ConnectionClose = true;
哪个控制KeepAlive
标头。
但是会妨碍您利用重用套接字的好处
另一个解决方案是:
var sp = ServicePointManager.FindServicePoint(new Uri("http://foo.bar/baz/123?a=ab"));
sp.ConnectionLeaseTimeout = 60*1000; // 1 minute
但是:
他没有说我们每次要发出请求时是否应该使用new Httpclient
,还是应该仍然使用静态的。
问题:
假设我想使用这个解决方案:
var sp = ServicePointManager.FindServicePoint(new Uri("http://foo.bar/baz/123?a=ab"));
sp.ConnectionLeaseTimeout = 60*1000; // 1 minute
我还应该使用static HttpClient
方法吗?还是每次我想打电话时都应该new HttpClient
?
或者 - 我应该为每个 scheme://basedns
创建静态/非静态 new HttpClient
吗?
他展示了问题,但他的结论没有展示整个正确的最终解决方案。
请注意 - 我问的是 .net 框架。不是 .net 核心。
【问题讨论】:
那是一篇描述第一个问题的非常老文章。它不包含第二个问题的任何解决方案。该解决方案是使用 HttpClientFactory 代替它保留自己的 HttpClientHandlers 池并定期回收它们。 @PanagiotisKanavos 您在文章中哪里看到了HttpClientFactory
这个词?我还认为您在谈论.net core(我的问题不是关于core),您在其中注入类型/名称的httpclient-或IhttpClientFactory(确实使用了处理程序池)。我的问题的第二个链接也解释了第二个问题。
我说这是一篇非常老文章。通过引入 HttpClientFactory 在接下来的 3 年里解决了这个问题。
【参考方案1】:
这是一篇非常老文章,它解释了为什么应该重用 HttpClient,但没有解释如何处理 DNS 更改。这在Singleton HttpClient? Beware of this serious behaviour and how to fix it 中有解释。这仍然只处理一个连接。
显而易见的答案是避免使用单例 HttpClient,但仍会在一段时间内重复使用它们。此外,不同的套接字用于连接到不同的服务器,这意味着我们确实需要重用(和回收)每个服务器的套接字。 解决方案后来以 HttpClientFactory 的形式出现。
好消息是 HttpClientFactory 是一个 .NET Standard 2.0 包,Microsoft.Extensions.Http 可供 .NET Core 和 .NET Old 使用,而不仅仅是 ASP.NET Core 应用程序。例如,我在控制台应用程序中使用它。
Steve Gordon 的 HttpClientFactory in ASP.NET Core 2.1 (PART 1) 是一个很好的介绍,所有他在 series 中的文章。
简而言之,每次我们想要一个 HttpClient 时,我们都会向工厂请求一个实例:
[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);
HttpClient 将工作委托给 SocketClientHandler。这就是需要重用的东西。 HttpClientFactory 生成 HttpClient 实例,这些实例重用来自套接字处理程序池的 Socket 处理程序。处理程序会定期回收以处理 DNS 更改。
更好的是,HttpClientFactory can be combined with Polly 为 HttpClient 实例添加重试逻辑。它通过配置处理程序在后台执行此操作。
【讨论】:
我了解 .net 核心解决方案,并且阅读了所有史蒂夫的系列文章。但它们都在.net核心中。我的问题清楚地说明了.net 框架而不是核心。所以回到这个问题,如果我不想将他的解决方案与var sp = ServicePointManager.FindServicePoint(new Uri("http://foo.bar/baz/123?a=ab")); sp.ConnectionLeaseTimeout = 60*1000; // 1 minute
一起使用,我应该如何实例化 httpClient
?多 ?单身的 ?对于每个端点?
它们是 .NET Standard,而不是 .NET Core。我在 .NET 旧控制台应用程序中使用它们。你的问题的答案是不要自己做,让工厂做。
我的问题不是关于 .net 标准,而是 4.6.1
使用 .NET 标准库。 .NET Standard 不是运行时,它是由 .NET Framework 或 .NET Core 等运行时提供的一组 API。面向 .NET Standard 的库可在与 .NET Standard 版本兼容的任何运行时中运行
@RoyiNamir 您已经在以一种或另一种方式使用 .NET Standard 2.0 库,无论是直接来自 Microsoft 还是其他 NuGet 包(如 JSON.NET)【参考方案2】:
ServicePointManager
在我看来是不稳定的,仅在 Windows 机器上有效。如果您从dotnet framework
转到dotnet core
,则实际上是死代码。我不会依赖我的实现。
我的建议是创建一个单例服务,只为HttpClient
的一个实例提供服务。
如果您使用的是 asp.net core
,他们会为您提供静态客户端的实现,并在此 document 中重新描述问题
【讨论】:
谢谢,但我没有使用核心。单例也会有 dns 更改的问题...(在 .net fw 中)。我已经知道如何在 .net 核心中处理它,但我的问题是针对非核心 @RoyiNamir 怎么样?单例意味着只有 1 个实例被有效地创建为您在整个应用程序中提供HttpClient
的单个实例。
我了解 .net 核心解决方案,并且阅读了所有史蒂夫的系列文章。但它们都在.net核心中。我的问题清楚地说明了.net 框架而不是核心。所以回到这个问题,如果我不想将他的解决方案与 var sp = ServicePointManager.FindServicePoint(new Uri("http://foo.bar/baz/123?a=ab")); sp.ConnectionLeaseTimeout = 60*1000; // 1 minute
一起使用,我应该如何实例化 httpClient ?多 ?单身的 ?对于每个端点?【参考方案3】:
我自己没有用过,但我想你可能正在寻找类似的东西:HttpClientFactoryLite。它派生了 Microsoft.Extensions.HttpClientFactory,然后删除了所有“自以为是”的部分(例如,DI、日志记录、类型化客户端等)以删除所有外部依赖项。
【讨论】:
以上是关于HttpClient 和套接字耗尽 - 澄清?的主要内容,如果未能解决你的问题,请参考以下文章
如何避免 .NET 中 HttpClient 的 DNS 失效问题?