每个服务端点的 HttpClient 实例化

Posted

技术标签:

【中文标题】每个服务端点的 HttpClient 实例化【英文标题】:HttpClient Instancing Per Service-Endpoint 【发布时间】:2015-04-30 20:11:01 【问题描述】:

在实例化 HttpClient 时,一个常见的建议是:

Use a singleton, do not dispose after each use。

但是,基于on this link,我看到了我认为暗示另一条规则的评论:

HttpClient 类实例充当发送 HTTP 请求的会话。 HttpClient 实例是应用于该实例执行的所有请求的设置集合。此外,每个 HttpClient 实例都使用自己的连接池,将其请求与其他 HttpClient 实例执行的请求隔离开来。

这让我想知道是否应该为我与之交互的每个服务端点创建一个 HttpClient 实例。 “服务端点”是指一个不同的基地址。以下每一项都是不同的“服务端点”:

“http://foo.net/api/Message/” “http://bar.com/api/Message/” “http://wow.gov/api/Message/” “http://now.com/api/Message/” “http://mom.org/api/Message/” “http://dog.com/api/Message/”

当然,如果我打算使用 HttpClient 的“BaseAddress”属性,并且如果我正在处理并发调用,那么每个“服务端点”都需要一个 HttpClient 实例。

但是,HttpClient 确实允许我明确指定绝对地址:

HttpClient client = new HttpClient(...);

client.PostAsJsonAsync("http://foo.net/api/Message/", ...);
client.PostAsJsonAsync("http://bar.com/api/Message/", ...);
client.PostAsJsonAsync("http://wow.gov/api/Message/", ...);
client.PostAsJsonAsync("http://now.com/api/Message/", ...);
client.PostAsJsonAsync("http://mom.org/api/Message/", ...);
client.PostAsJsonAsync("http://dog.com/api/Message/", ...);

上面的代码有效,这正是我正在构建的当前应用程序想要的。但是问题仍然存在……如果我将一个 HttpClient 用于我的应用程序与之通信的所有服务端点,我是否做错了什么?

我真的需要上面引用中提到的“连接池隔离”吗?

【问题讨论】:

因为我让许多端点共享同一个连接池。您能详细说明一下吗?端点是什么意思?你将如何使用HttpClient?调用外部 HTTP 端点? 从个人经验来看,我认为答案主要取决于你需要什么样的并发和吞吐量。我发现 HttpClient 的单个实例在一段时间后陷入困境,而多个实例 可以 提供帮助。话虽如此,如果有足够的容量,您将耗尽服务器的资源,可能需要横向扩展。 我已经修改了我的问题以澄清“服务端点”以及我关心的问题。是的,我将使用 HttpClient 调用外部 HTTP 端点。 【参考方案1】:

但我想知道我是否会损害 HttpClient 因为我让许多端点共享同一个连接 游泳池。

不,我认为HttpClient 的单个实例不会耗尽您的资源,但这实际上取决于您将发出多少并发请求。 HttpClient 旨在为并发请求提供服务,通过使用异步 API (XXXAsync),您可以做到这一点。

我建议不要忘记将ServicePointManager.DefaultConnectionLimit 设置为更高的数字,因为它的默认值为 2(并发请求)。

另外,如果您认为使用单个实例很快就会碰壁,我建议一如既往地分析您的应用程序以了解确切的瓶颈。

【讨论】:

这 - “我建议不要忘记将 ServicePointManager.DefaultConnectionLimit 设置为更高的数字,因为它的默认值为 2(并发请求)。” - 是不正确的。默认值为 2 ...但这意味着没有限制(更明智的 API 以类似的方式使用值 -1 或 0)。除非您自己设置 DefaultConnectionLimit,否则 HttpClient 将毫无遗憾地创建数千个并发连接。 @Test_me 谁来保证?这绝对不是what the docs say:ServicePoint 对象允许的最大并发连接数。另外,请阅读this。 文档不正确:“奇怪的是,根据 MSDN,连接限制的默认值为 2。我还使用调试器检查了这一点,指出确实 2 是“ ***.com/questions/16194054/… @Test_me 我绝对不会依赖 *** 上一个人的单一遗嘱。他在哪里进行测试?是在他的本地 PC 上还是在服务器上?这会有所不同。除此之外,您指的是实现细节。如果文档说它应该受到尊重而目前没有,那就是一个错误。如果在下一个 .NET 版本或 .NET 核心中发生这种变化会怎样?你绝对不能把钱花在不明确设置这个值上 为什么哦,为什么围绕 httpclient 有这么多冲突和垃圾数据?太疯狂了。【参考方案2】:

我也遇到了同样的问题。

还有我的调查结果:

    如果您需要在请求期间更改其非线程安全设置 (DefaultRequestHeaders, BaseAddress),请使用 HttpClient 的单独实例。为每个实例创建具有一组特定设置的单独实例将是有用且安全的。更多细节:HttpClient–Is It Really Thread-Safe?

    好的,我创建了HttpClient 的特定实例,但我不想使用BaseAdress。从第 1 点开始)我需要为每个唯一的 BaseAdress(主机)使用新的 HttpClient 实例。

    但是,如果我使用HttpClient 向多个主机请求,而没有BaseAdress 的绝对地址呢? 您可以跟踪从HttpClientSocketsHttpHanlderHttpConnectionPoolManager (dotnet source code) 的调用,并查看它为每个主机创建单独的“HttpConnectionPool”并将池添加到“ConcurrentDictionary”_pools。这意味着无论如何我们对每个唯一主机都有单独的池。 这就是为什么我更喜欢在不使用 BaseAdress 的情况下为多个主机使用单个 HttpClient 实例。

【讨论】:

以上是关于每个服务端点的 HttpClient 实例化的主要内容,如果未能解决你的问题,请参考以下文章

修改每个请求的请求标头 C# HttpClient PCL

重新使用 HttpClient 但每个请求的超时设置不同?

具有不同身份验证标头的 HttpClient 单个实例

ASP.NET MVC 中的 HttpClient 单例实现

来自 C# HttpClient 针对 Spring 服务器 JWT 令牌的身份验证

HttpClient 实例化和请求都需要使用 [重复]