【问题标题】:Should I cache and reuse HttpClient created from HttpClientFactory?我应该缓存和重用从 HttpClientFactory 创建的 HttpClient 吗?
【发布时间】:2019-02-08 17:13:08
【问题描述】:

我们可以在这里阅读YOU'RE USING HTTPCLIENT WRONG AND IT IS DESTABILIZING YOUR SOFTWARE,我们不应该为每个 http 请求创建和处置 HttpClient。相反,它应该被缓存和重用(例如作为 DI 容器中的 Singleton)。以及HttpClient 的官方 .NET 文档:

HttpClient 旨在被实例化一次并在应用程序的整个生命周期中重复使用。为每个请求实例化一个 HttpClient 类将耗尽重负载下可用的套接字数量。这将导致 SocketException 错误。下面是一个正确使用 HttpClient 的例子。

推荐使用HttpClientFactory,但是看了之后:

  public interface IHttpClientFactory
  {
    /// <summary>
    /// Creates and configures an <see cref="T:System.Net.Http.HttpClient" /> instance using the configuration that corresponds
    /// to the logical name specified by <paramref name="name" />.
    /// </summary>
    /// <param name="name">The logical name of the client to create.</param>
    /// <returns>A new <see cref="T:System.Net.Http.HttpClient" /> instance.</returns>
    /// <remarks>
    /// <para>
    /// Each call to <see cref="M:System.Net.Http.IHttpClientFactory.CreateClient(System.String)" /> is guaranteed to return a new <see cref="T:System.Net.Http.HttpClient" />
    /// instance. Callers may cache the returned <see cref="T:System.Net.Http.HttpClient" /> instance indefinitely or surround
    /// its use in a <langword>using</langword> block to dispose it when desired.
    /// </para>
    /// <para>
    /// The default <see cref="T:System.Net.Http.IHttpClientFactory" /> implementation may cache the underlying
    /// <see cref="T:System.Net.Http.HttpMessageHandler" /> instances to improve performance.
    /// </para>
    /// <para>
    /// Callers are also free to mutate the returned <see cref="T:System.Net.Http.HttpClient" /> instance's public properties
    /// as desired.
    /// </para>
    /// </remarks>
    HttpClient CreateClient(string name);
  }

它说每次调用都会创建一个 HttpClient 实例,调用者可以缓存它。

保证每次调用 IHttpClientFactory.CreateClient 都会返回一个新的 HttpClient 实例。调用者可以无限期缓存返回的实例或包围 它在 using 块中的使用以在需要时对其进行处理。

所以问题是我应该完全依赖 HttpClientFactory 还是应该缓存创建的 HttpClient

在我们的项目中,我们每次发出请求时都会使用 HttpClientFactory.CreateClient,他仍然会遇到套接字异常。

【问题讨论】:

  • 文档说它每次都会创建一个新实例,并且您知道应该使用单个实例...所以显然您应该缓存?我在这里错过了什么
  • @BradleyDotNET 有很多关于 HttpClientFactory 的文章,但没有人这么说。请理解我的惊愕。
  • @mardok 不,那是 HttpClientFactory 的工作
  • @BradleyDotNET 它是 HttpClientFactory 负责缓存和 刷新 实例。它的存在是为了让人们不必缓存
  • @mardok HttpClientFactory 缓存实际执行网络操作的 HttpMessageHandler。

标签: c# .net-core httpclient dotnet-httpclient


【解决方案1】:

HttpClient 只是IDisposable,因为它的HttpMessageHandlerIDisposable。实际上,应该是HttpMessageHandler 应该是长寿的。

HttpClientFactory 通过在内部保持一个长期存在的HttpMessageHandler 来工作。每当您请求HttpClient 时,它都会使用长寿命的HttpMessageHander,并告诉HttpClient 不要HttpClient 被处置时处置它。

你可以看到on GitHub:

public HttpClient CreateClient(string name)
{
    // ...

    // Get a cached HttpMessageHandler
    var handler = CreateHandler(name);

    // Give it to a new HttpClient, and tell it not to dispose it
    var client = new HttpClient(handler, disposeHandler: false);

    // ...

    return client;
}

因此,从技术上讲,无论您是缓存 HttpClient 还是立即处理它都无关紧要 - 处理它不会做任何事情(因为它被告知不要处理它的 HttpClientHandler,因为这是由 @ 管理的987654337@).

关于处理HttpClientMSDN says

不需要处理客户。 Disposal 取消传出请求并保证在调用 Dispose 后不能使用给定的 HttpClient 实例。 IHttpClientFactory 跟踪和释放 HttpClient 实例使用的资源。 HttpClient 实例通常可以被视为不需要处理的 .NET 对象。

长时间保持单个 HttpClient 实例处于活动状态是 IHttpClientFactory 开始之前使用的常见模式。迁移到 IHttpClientFactory 后,这种模式就变得不必要了。

我怀疑您看到的SocketExceptions 有不同的原因。也许问一个关注他们的新问题?

【讨论】:

  • (facepalm) 我正在调查一个实际上并不存在的用户错误,所以我的大脑变成了一个黑洞
  • @PanagiotisKanavos 我确实觉得很奇怪,您在 cmets 中的推理与您在 cmets 中关于原始问题的说法相反!不用担心,我会删除我的回复。
【解决方案2】:

ASP.NET Core 2.2 版本中,情况发生了很好的变化。 预计使用HttpClient 的方式是仅通过DI,它在内部使用HttpClientFactory 为您处理所有必要的缓存。以下文档文章已更新以反映这些新用例:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-2.2

此外,来自 ASP.NET Core 团队的@RyanNowak 在以下 ASP.Net Core 社区站立会议中介绍了所有这些更改:https://www.youtube.com/watch?v=Lb12ZtlyMPg 如果您还没有看过它,我强烈建议您观看它,因为它信息量很大,而且很有教育意义。

这是一个展示用法的小示例。在Startup.ConfigureServices方法调用中:

services.AddHttpClient();

注意:有多种使用模式,这是最基本的一种。查看文档以了解其他模式,这可能更适合您的需求。

稍后,在您想从哪里发出 http 请求的课程中,依赖 IHttpClientFactory 并让 DI 在必要时为您实例化它。以下是 Microsoft Docs 中的示例:

public class BasicUsageModel : PageModel
{
    private readonly IHttpClientFactory _clientFactory;

    public IEnumerable<GitHubBranch> Branches { get; private set; }

    public bool GetBranchesError { get; private set; }

    public BasicUsageModel(IHttpClientFactory clientFactory)
    {
        _clientFactory = clientFactory;
    }

    public async Task OnGet()
    {
        var request = new HttpRequestMessage(HttpMethod.Get, 
            "https://api.github.com/repos/aspnet/docs/branches");
        request.Headers.Add("Accept", "application/vnd.github.v3+json");
        request.Headers.Add("User-Agent", "HttpClientFactory-Sample");

        var client = _clientFactory.CreateClient();

        var response = await client.SendAsync(request);

        if (response.IsSuccessStatusCode)
        {
            Branches = await response.Content
                .ReadAsAsync<IEnumerable<GitHubBranch>>();
        }
        else
        {
            GetBranchesError = true;
            Branches = Array.Empty<GitHubBranch>();
        }                               
    }
}

【讨论】:

猜你喜欢
  • 2018-11-27
  • 1970-01-01
  • 2023-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-09
  • 1970-01-01
  • 2016-01-05
相关资源
最近更新 更多