为什么在 WebApi 上下文中的 using 块中使用 HttpClient 是错误的?

Why using HttpClient in a using block IS WRONG in WebApi context?

所以,问题是为什么在 using 块中使用 HttpClient 是错误的,但在 WebApi 上下文中?

我一直在阅读这篇文章 Don't Block on Async Code。其中我们有以下示例:

public static async Task<JObject> GetJsonAsync(Uri uri)
{
  // (real-world code shouldn't use HttpClient in a using block; this is just example code)
  using (var client = new HttpClient())
  {
    var jsonString = await client.GetStringAsync(uri);
    return JObject.Parse(jsonString);
  }
}

// My "top-level" method.
public class MyController : ApiController
{
  public string Get()
  {
    var jsonTask = GetJsonAsync(...);
    return jsonTask.Result.ToString();
  }
}

评论// (real-world code shouldn't use HttpClient in a using block; this is just example code)刚刚触动了我。我一直都是这样使用HttpClient的。

我检查的下一件事是 Microsoft's documentation on HttpClient Class。 在其中,我们有以下语句和提供的源示例:

HttpClient is intended to be instantiated once and re-used throughout the life of an application. Instantiating an HttpClient class for every request will exhaust the number of sockets available under heavy loads. This will result in SocketException errors. Below is an example using HttpClient correctly.

public class GoodController : ApiController
{
    private static readonly HttpClient HttpClient;

    static GoodController()
    {
        HttpClient = new HttpClient();
    }
}

那不是每次请求都会调用构造函数,每次都会创建一个新的HttpClient吗?

谢谢!

实际上写这个问题我注意到微软提供的代码示例中的静态构造函数。现在这一切都有意义了。

Static Constructors 用于初始化任何静态数据,或执行只需要执行一次的特定操作。在创建第一个实例或引用任何静态成员之前自动调用它。

在 WebAPI 的上下文中,静态构造函数被调用 仅一次 因此只创建一个 HttpClient 并将其重新用于所有其他请求。

我再也不会在生产代码中使用 using(HttpClient....)

这是一篇关于 HttpClient 错误用法的好文章 - YOU'RE USING HTTPCLIENT WRONG AND IT IS DESTABILIZING YOUR SOFTWARE

这个问题的答案有点长...

最初,官方建议在 using 块中使用 HttpClient。但是这个 caused problems at scale,本质上是在 TIME_WAIT 状态下耗尽了很多连接。

所以,官方建议改为使用静态HttpClient。但这导致了 it would never correctly handle DNS updates.

的问题

因此,ASP.NET 团队提出了 IHttpClientFactory in .NET Core 2.1,因此代码(或至少现代平台上的代码 运行)可以重用 HttpClient 个实例(或者,更恰当地说,这些实例的 消息处理程序 ),避免了 TIME_WAIT 问题,而且还定期关闭这些连接以避免 DNS 问题。

但是,与此同时,.NET 团队想出了 SocketsHttpHandler also in .NET Core 2.1 进行了连接池。

因此,在现代平台上,您可以使用 IHttpClientFactory 或 static/singleton HttpClient。在较旧的平台(包括 .NET Framework)上,您将使用 static/singleton HttpClient 并忍受 DNS 问题或 use other workarounds.