HttpClient 的使用准则

System.Net.Http.HttpClient 类用于发送 HTTP 请求以及从 URI 所标识的资源接收 HTTP 响应。 HttpClient 实例是应用于该实例执行的所有请求的设置集合,每个实例使用自身的连接池,该池将其请求与其他请求隔离开来。 从 .NET Core 2.1 开始,SocketsHttpHandler 类提供实现,使行为在所有平台上保持一致。

DNS 行为

HttpClient 仅在创建连接时解析 DNS 条目。 它不跟踪 DNS 服务器指定的任何生存时间 (TTL)。 如果 DNS 条目定期更改(这可能在某些方案中发生),客户端将不会遵循这些更新。 要解决此问题,可以通过设置 PooledConnectionLifetime 属性来限制连接的生存期,以便在替换连接时重复执行 DNS 查找。 请考虑以下示例:

var handler = new SocketsHttpHandler
{
    PooledConnectionLifetime = TimeSpan.FromMinutes(15) // Recreate every 15 minutes
};
var sharedClient = new HttpClient(handler);

上述 HttpClient 配置为重复使用连接 15 分钟。 PooledConnectionLifetime 指定的时间范围过后,系统会关闭连接,然后创建一个新连接。

共用连接

HttpClient 的连接池链接到其基础 SocketsHttpHandler。 释放 HttpClient 实例时,它会释放池中的所有现有连接。 如果稍后向同一服务器发送请求,则必须重新创建一个新连接。 因此,创建不必要的连接会导致性能损失。 此外,TCP 端口不会在连接关闭后立即释放。 (有关这一点的详细信息,请参阅 RFC 9293 中的 TCP TIME-WAIT。)如果请求速率较高,则可用端口的操作系统限制可能会耗尽。 为了避免端口耗尽问题,建议HttpClient 实例重用于尽可能多的 HTTP 请求。

为了对建议的 HttpClient 在生存期管理方面的使用进行总结,应使用长期客户端,并设置 PooledConnectionLifetime(.NET Core 和 .NET 5+)或由 IHttpClientFactory 创建的短期客户端

  • 在 .NET Core 和 .NET 5+ 中:

    • 根据预期的 DNS 更改,使用 staticsingletonHttpClient 实例,并将 PooledConnectionLifetime 设置为所需间隔(例如 2 分钟)。 这可以解决端口耗尽和 DNS 更改两个问题,而且不会增加 IHttpClientFactory 的开销。 如果需要模拟处理程序,可以单独注册它。

    提示

    如果只使用有限数量的 HttpClient 实例,这也是可接受的策略。 重要的是,它不是随每个请求一起创建和释放的,因为它们每一个都包含一个连接池。 对于具有多个代理的方案,或者要在不完全禁用 Cookie 处理的情况下分离 Cookie 容器,必须使用多个实例。

    • 使用 IHttpClientFactory,可以针对不同的用例使用多个以不同方式配置的客户端。 但请注意,工厂创建的客户端生存期较短,一旦创建客户端,工厂就不再可以控制它。

      工厂合并 HttpMessageHandler 实例,如果其生存期尚未过期,则当工厂创建新的 HttpClient 实例时,可以从池中重用处理程序。 这种重用避免了任何套接字耗尽问题。

      如果需要 IHttpClientFactory 提供的可配置性,我们建议使用类型化客户端方法

  • 在 .NET Framework 中,使用 IHttpClientFactory 管理 HttpClient 实例。 如果不使用工厂,而是改为自行为每个请求创建新的客户端实例,则可能耗尽可用的端口。

    提示

    如果应用需要 Cookie,请考虑禁用自动 Cookie 处理或避免使用 IHttpClientFactory。 共用 HttpMessageHandler 实例会导致共享 CookieContainer 对象。 意外的 CookieContainer 对象共享通常会导致错误的代码。

有关通过 IHttpClientFactory 管理 HttpClient 生存期的详细信息,请参阅 IHttpClientFactory 指南

静态客户端的复原能力

可使用以下模式将 static 或单一实例客户端配置为使用任意数量的复原管道:

using System;
using System.Net.Http;
using Microsoft.Extensions.Http;
using Microsoft.Extensions.Http.Resilience;
using Polly;

var retryPipeline = new ResiliencePipelineBuilder<HttpResponseMessage>()
    .AddRetry(new HttpRetryStrategyOptions
    {
        BackoffType = DelayBackoffType.Exponential,
        MaxRetryAttempts = 3
    })
    .Build();

var socketHandler = new SocketsHttpHandler
{
    PooledConnectionLifetime = TimeSpan.FromMinutes(15)
};
var resilienceHandler = new ResilienceHandler(retryPipeline)
{
    InnerHandler = socketHandler,
};

var httpClient = new HttpClient(resilienceHandler);

前面的代码:

  • 依赖于 Microsoft.Extensions.Http.Resilience NuGet 包。
  • 指定一个暂时性 HTTP 错误处理程序,该处理程序配置了重试管道,每次尝试都会指数退避延迟间隔。
  • 定义 socketHandler 的入池连接生存期为 15 分钟。
  • 使用重试逻辑将 socketHandler 传递给 resilienceHandler
  • 根据 resilienceHandler 实例化 HttpClient

重要

Microsoft.Extensions.Http.Resilience 库当前标记为实验性,将来可能会更改。

另请参阅