在 ASP.NET Core 应用程序中使用多个 HttpClient 对象

Posted

技术标签:

【中文标题】在 ASP.NET Core 应用程序中使用多个 HttpClient 对象【英文标题】:Using multiple HttpClient objects in an ASP.NET Core application 【发布时间】:2020-01-03 10:09:44 【问题描述】:

当有多个客户端(公司)使用 ASP.NET Core 应用程序并且必须在每个公司的 ASP.NET 应用程序中维护具有相应客户端证书的单独 HttpClient 实例时,最佳应用程序设计是什么?

首先,关于应用程序的一些重要事实:

这是一个 ASP.NET Core 3.1 应用程序。 使用此 ASP.NET Core 应用程序的每个公司都需要先上传自己的客户端证书 在注册期间。证书和相应的密码是 存储在数据库中。通信需要证书 与一些必须代表代表通知的外部 Web 服务 该公司在 提到了 ASP.NET Core 应用程序。换句话说,对于某些动作 公司在 ASP.NET Core 应用程序中执行,一些外部 web 必须使用 POST 方法和相应的正文通知服务和 必须处理并返回其响应。 注册后, 公司可以开始向上述 ASP.NET Core 发送请求 应用。此 ASP.NET Core 应用程序应获取 数据库中对应的公司证书,创建 HttpClient 对象并为其添加证书,准备请求数据并调用外部 Web 服务 通知。那么来自这个外部网络服务的响应应该是 处理并作为对原始公司请求的响应而返回。 当然,可以有来自不同公司的同时请求,但也可以同时来自不同公司办公室的同一公司的请求 - 来自同一公司的所有请求都使用相同的公司客户证书,但请求数据( body) 通知外部网络服务总是不同的。

据我所知,为每次调用外部 Web 服务创建新的 HttpClient 对象并不是最佳做法。我考虑过 HttpClientFactory,但在我的情况下,每个 HttpClient 对象都必须包含公司的相应客户端证书。我知道 ASP.NET Core 支持命名 HttpClient 来为每个公司维护单独的 HttpClient,但是 AFAIK 这个命名客户端只能在 Startup 类中创建。这还不够,因为可以在 ASP.NET 应用程序运行时随时注册新公司,公司可以在应用程序运行时上传新证书,使现有命名客户端无效等。

我正在考虑为每个公司维护一个包含一个 HttpClient 对象的静态列表。当发起对公司的第一次请求时,会使用相应的客户端证书创建新的 HttpClient 并将其添加到此列表中。对于来自同一公司(或办公室)的所有后续请求,从提到的静态列表中获取相应的 HttpClient 并重用。当然,我必须在公司的 HttpClient 实例上建立一些锁定,以便在来自同一公司的同时请求的情况下它不会中断。我对这种设计的一个担忧是可能有几百家公司,而且这个 http 客户端列表可能很长。

您还有其他想法或建议吗?

【问题讨论】:

拥有多个HttpClients 的问题实际上是内部HttpClientHandler 使连接和保持套接字打开。由于您必须新建 HttpClientHandler 来设置客户端证书,因此将这些实例池化并将它们设置为具有非常短的生命周期可能更有意义,以便它们老化并快速关闭使用的套接字。但总的来说,我觉得这更像是 ASP.NET Core GitHub 的一个问题,因为我不认为 HttpClientHttpClientFactory 是在考虑这种用例的情况下开发的。 josefottosson.se/… 【参考方案1】:

我正在考虑为每个公司维护一个包含一个 HttpClient 对象的静态列表。

在较高的层次上,我认为每个公司单个实例是正确的方法,但魔鬼在(实施)细节中。

当然,我必须建立一些锁定..

如果您使用的是为这种用途而构建的类型,则不会。 ConcurrentDictionary,特别是它的GetOrAdd method,将为您提供一个键控存储机制,其中项目是惰性创建的,并且是线程安全且无锁的。

我对这种设计的一个担忧是可能有几百家公司,而这个 http 客户端列表可能会很长。

我不知道是否有一个具体的数字需要您开始担心(我相信取决于操作系统和硬件),但至少根据 the article that made the HttpClient singleton advice famous,它是数千个,而不是数百个。

“在生产场景中,我的socket数量平均在4000左右,峰值超过5000,有效地压垮了服务器上的可用资源,从而导致服务崩溃。实施更改后,使用中的套接字从平均超过 4000 个下降到始终少于 400 个,通常在 100 个左右。”

不过,如果这是一个问题,您可以做的一件事来缓解它是允许那些缓存的 HttpClient 实例偶尔过期。 (这也应该减轻that other famous problem 的影响。)不幸的是,ConcurrentDictionary 没有提供开箱即用的功能。 MemoryCache 支持,但不直接支持像 ConcurrentDictionary 这样的惰性 GetOrAdd 语义。为了两全其美,请查看LazyCache。

【讨论】:

以上是关于在 ASP.NET Core 应用程序中使用多个 HttpClient 对象的主要内容,如果未能解决你的问题,请参考以下文章

在 ASP.NET Core Identity 中使用 Authorize 属性检查多个策略之一

TSQL 多个连接与来自 ASP.NET Core 的多个请求?

具有多个构造函数的 ASP.NET Core 依赖注入

在 ASP.NET Core 中使用多个身份验证方案

为啥要在 ASP.Net Core 中获取多个 IMemoryCache 实例?

ASP.NET Core 中的 Angular 应用 6:多个模块匹配