在 ASP.NET 核心应用程序中使用多个 HttpClient 对象
Using multiple HttpClient objects in an ASP.NET Core application
当有多个客户(公司)使用 ASP.NET 核心应用程序并且必须在 ASP.NET 应用程序中维护具有相应客户端证书的单独 HttpClient 实例时,最佳应用程序设计是什么对于这些公司中的每一个?
首先,关于申请的一些重要事实:
- 这是一个 ASP.NET Core 3.1 应用程序。
- 使用此ASP.NET核心应用程序的每个公司都需要先上传自己的客户端证书
在注册期间。证书和对应的密码是
存储在数据库中。通信需要证书
与一些必须代表通知的外部网络服务
公司在
提到 ASP.NET 核心应用程序。换句话说,对于某些动作
公司在 ASP.NET 核心应用程序中执行,一些外部 Web
必须使用 POST 方法和相应的正文通知服务
它的响应必须被处理并返回。
- 注册后,
公司可以开始向提到的 ASP.NET 核心发送请求
应用。此 ASP.NET 核心应用程序应获得
从数据库中相应的公司证书,创建
HttpClient 对象并向其添加证书,准备请求数据并调用外部 Web 服务
通知。那么这个外部网络服务的响应应该是
处理并返回作为对原始公司请求的响应。
- 当然,可以有来自不同公司的同时请求,但也可以有来自不同公司办公室的同时请求来自同一公司——来自同一公司的所有请求都使用同一公司客户证书,但请求通知外部网络服务的数据(正文)将始终不同。
据我所知,为每次调用外部 Web 服务创建新的 HttpClient 对象并不是最佳做法。我考虑过 HttpClientFactory,但在我的例子中,每个 HttpClient 对象都必须包含公司相应的客户端证书。我知道 ASP.NET Core 支持命名的 HttpClient 来为每个公司维护单独的 HttpClient,但是据我所知,这个命名的客户端只能在 Startup class 中创建。这还不够,因为当 ASP.NET 应用 运行 时,可以随时注册新公司,当应用 运行 时,公司可以上传新证书,从而使现有命名客户端无效,等等。
我正在考虑为每个公司维护一个包含一个 HttpClient 对象的静态列表。当发起对公司的第一个请求时,将使用相应的客户端证书创建新的 HttpClient 并将其添加到此列表中。对于来自同一公司(或办公室)的所有后续请求,相应的 HttpClient 从提到的静态列表中获取并重新使用。当然,我必须在公司的 HttpClient 实例上建立一些锁定,以便在同一公司同时发出请求时它不会中断。我对这种设计的一个担忧是,可能有几百家公司,而这个 http 客户端列表可能会很长。
您还有什么想法或建议吗?
I am considering maintaining a static list containing one HttpClient object for each company.
在高层次上,我认为每个公司一个实例是正确的方法,但魔鬼在(实施)细节中。
Of course, I would have to establish some locking..
如果您使用的是专为此类用途而构建的类型,则不会。 ConcurrentDictionary, and specifically its GetOrAdd method,将为您提供一个键控存储机制,其中项目是延迟创建的,并且是线程安全和无锁的。
One concern I have with this design is that there might be couple of hundred companies and this list of http clients can be quite long.
我不知道您是否需要开始担心的具体数字(我相信取决于 OS 和硬件),但至少根据 the article that made the HttpClient singleton advice famous,它在数千,而不是数百。
"In the production scenario I had the number of sockets was averaging around 4000, and at peak would exceed 5000, effectively crushing the available resources on the server, which then caused services to fall over. After implementing the change, the sockets in use dropped from an average of more than 4000 to being consistently less than 400, and usually around 100."
不过,如果这是一个问题,您可以做的一件事就是让那些缓存的 HttpClient
实例偶尔过期。 (这也应该减轻 that other famous problem.) Unfortunately, ConcurrentDictionary
doesn't provide that capability out of the box. MemoryCache does, but doesn't directly support the lazy GetOrAdd
semantics like ConcurrentDictionary
. For the best of both worlds, have a look at LazyCache.
当有多个客户(公司)使用 ASP.NET 核心应用程序并且必须在 ASP.NET 应用程序中维护具有相应客户端证书的单独 HttpClient 实例时,最佳应用程序设计是什么对于这些公司中的每一个?
首先,关于申请的一些重要事实:
- 这是一个 ASP.NET Core 3.1 应用程序。
- 使用此ASP.NET核心应用程序的每个公司都需要先上传自己的客户端证书 在注册期间。证书和对应的密码是 存储在数据库中。通信需要证书 与一些必须代表通知的外部网络服务 公司在 提到 ASP.NET 核心应用程序。换句话说,对于某些动作 公司在 ASP.NET 核心应用程序中执行,一些外部 Web 必须使用 POST 方法和相应的正文通知服务 它的响应必须被处理并返回。
- 注册后, 公司可以开始向提到的 ASP.NET 核心发送请求 应用。此 ASP.NET 核心应用程序应获得 从数据库中相应的公司证书,创建 HttpClient 对象并向其添加证书,准备请求数据并调用外部 Web 服务 通知。那么这个外部网络服务的响应应该是 处理并返回作为对原始公司请求的响应。
- 当然,可以有来自不同公司的同时请求,但也可以有来自不同公司办公室的同时请求来自同一公司——来自同一公司的所有请求都使用同一公司客户证书,但请求通知外部网络服务的数据(正文)将始终不同。
据我所知,为每次调用外部 Web 服务创建新的 HttpClient 对象并不是最佳做法。我考虑过 HttpClientFactory,但在我的例子中,每个 HttpClient 对象都必须包含公司相应的客户端证书。我知道 ASP.NET Core 支持命名的 HttpClient 来为每个公司维护单独的 HttpClient,但是据我所知,这个命名的客户端只能在 Startup class 中创建。这还不够,因为当 ASP.NET 应用 运行 时,可以随时注册新公司,当应用 运行 时,公司可以上传新证书,从而使现有命名客户端无效,等等。
我正在考虑为每个公司维护一个包含一个 HttpClient 对象的静态列表。当发起对公司的第一个请求时,将使用相应的客户端证书创建新的 HttpClient 并将其添加到此列表中。对于来自同一公司(或办公室)的所有后续请求,相应的 HttpClient 从提到的静态列表中获取并重新使用。当然,我必须在公司的 HttpClient 实例上建立一些锁定,以便在同一公司同时发出请求时它不会中断。我对这种设计的一个担忧是,可能有几百家公司,而这个 http 客户端列表可能会很长。
您还有什么想法或建议吗?
I am considering maintaining a static list containing one HttpClient object for each company.
在高层次上,我认为每个公司一个实例是正确的方法,但魔鬼在(实施)细节中。
Of course, I would have to establish some locking..
如果您使用的是专为此类用途而构建的类型,则不会。 ConcurrentDictionary, and specifically its GetOrAdd method,将为您提供一个键控存储机制,其中项目是延迟创建的,并且是线程安全和无锁的。
One concern I have with this design is that there might be couple of hundred companies and this list of http clients can be quite long.
我不知道您是否需要开始担心的具体数字(我相信取决于 OS 和硬件),但至少根据 the article that made the HttpClient singleton advice famous,它在数千,而不是数百。
"In the production scenario I had the number of sockets was averaging around 4000, and at peak would exceed 5000, effectively crushing the available resources on the server, which then caused services to fall over. After implementing the change, the sockets in use dropped from an average of more than 4000 to being consistently less than 400, and usually around 100."
不过,如果这是一个问题,您可以做的一件事就是让那些缓存的 HttpClient
实例偶尔过期。 (这也应该减轻 that other famous problem.) Unfortunately, ConcurrentDictionary
doesn't provide that capability out of the box. MemoryCache does, but doesn't directly support the lazy GetOrAdd
semantics like ConcurrentDictionary
. For the best of both worlds, have a look at LazyCache.