3

我相信我们都读过这篇在 2016 年引起轰动的文章:https ://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

对于那些没有的人,总结一下(强调我的):

您应该在应用程序的整个生命周期内共享一个 HttpClientHttpClient实例,而不是为每次执行创建一个新实例。

现在,考虑一个 ASP.NET Core 应用程序,它使用HttpClient. 它的使用HttpClient一般分为两种情况:

  1. 未经身份验证或使用网站自己的凭据的传出请求 - 在这种情况下HttpClient,程序的所有部分都可以共享一个请求。

  2. 代表网站当前访问者之一发出的传出请求 - 或使用特定于请求的凭据的请求。

    • 虽然可以使用单个HttpClient实例,但您必须注意不要改变其状态,例如通过设置(例如通过使用扩展方法)。DefaultRequestHeadersSetBearerToken

几乎所有HttpClient在 ASP.NET Core 中使用的指南都说:

  • 使用类型化客户端(接受HttpClient实例作为构造函数参数的 POCO,以及任何其他 DI 服务)。
  • 使用 注册这些类型化客户端services.AddHttpClient<TClient>()
    • 这将注册TClient瞬态实例。
    • 这将注册ITypedHttpClientFactory<TClient>瞬态实例。
    • 这将注册IHttpClientFactory单例
    • 这将注册IHttpMessageHandlerFactory单例

有了现在讨论的内容,我将把你的注意力引向一个矛盾:

  • 著名的博客文章——现在是微软自己的文档——说这些HttpClient实例必须是长寿命的。
  • ASP.NET Core 的HttpClientDI 系统确实确保HttpClient实例的生命周期很短。

但是,如果博客文章真的应该谈论底层HttpMessageHandler(这是真正的HttpClient 实现,它只是由薄壳包裹class HttpClient)而不是外部,这可能class HttpClient吗?

令人困惑的是,人们也报告了使用长寿命HttpClient实例的问题,并介绍了其他所有涉及的解决方法,static class ServicePointManager这让我感到不舒服:

我的问题:

  • HttpClient实例真的意味着寿命长还是寿命短?
  • 还是只是HttpMessageHandler需要长期存在的实例?
  • 如果HttpClientHttpMessageHandler实例意味着长寿:
    • 如何正确解决ServicePointManager两个链接博客文章中提出的问题?
    • 我是否绝对必须避免使用HttpClient.DefaultRequestHeaders,而是明确设置每个Authorization(Cookies or Credentials) 标头HttpRequestMessage?有没有其他更省事的方法?
    • 我可以使用我自己的添加这些标题DelegatingHandler吗?那么这个提议的处理程序的 DI 注册应该是什么?
  • 如果HttpClient实例(但不是HttpMessageHandler实例)意味着寿命短:

    • ...那么可以使用HttpClient.DefaultRequestHeaders(and SetBearerToken()) 吗?
    • HttpMessageHandler那么实例的生命周期策略是什么?
    • 我还能使用HttpClient.DefaultRequestHeaders(and SetBearerToken()) 吗?
  • 如果HttpClient实例 HttpMessageHandler实例的生命周期很短:

    • 那么 2016 年原始博客文章中提到的问题呢?
      • 是否IHttpClientFactory采取任何措施来缓解这些问题?
4

1 回答 1

1

根本问题在于套接字连接。new HttpClient()为每个请求创建一个新端口,并使它们在 TIME_WAIT 状态下打开: 在此处输入图像描述

这不是他们坐的正确状态,它占用了一个套接字而不被重用。当服务器用完套接字时,.net 会抛出 a ,SocketException因为没有打开的套接字就无法建立连接。这也称为端口耗尽,因为端口用完了套接字。

IHttpClientFactory 将端口保持在 ESTABLISHED 状态,如果有另一个请求进来,就会重用它们,如果 4 分钟内没有请求进来,它会关闭它们。 在此处输入图像描述

您的问题最终都是关于 HTTP 和套接字连接的,但您使用 .net 类来描述它。IHttpClientFactory 使用 HttpClientHandlers 池管理 tcp 套接字连接,因此您不必这样做。您其余问题的答案在这里:https ://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests

当需要创建新的 HttpClients 时,工厂将重用处理程序。您仍然可以通过自己的实现设置动态值,例如不记名令牌,例如编写自己的Get<T>方法,该方法接受 astring bearerToken然后设置 Authorization 标头,以便您可以将该代码保存在一个地方。

...我会让你注意一个矛盾:

The famous blog article - and now Microsoft's own documentation - says the HttpClient instances must be long-life'd.
ASP.NET Core's HttpClient DI system really makes sure that HttpClient instances are short-life'd.

你在哪里看到微软关于 IHttpClientFactory 的文档说 httpclients 需要长寿?通过工厂,它们被限定在 DI 中,这意味着它们在请求结束时被处理掉。这就是为什么重用处理程序是安全的,但在运行时修改客户端。

于 2021-08-22T04:31:44.097 回答