【问题标题】:Long lived HttpClient created by HttpClientFactory由 HttpClientFactory 创建的长寿 HttpClient
【发布时间】:2020-03-05 17:14:42
【问题描述】:

我听说HttpMessageHandlers 每 2 分钟回收一次,但我不确定是否将新的分配给现有的 HttpClient

我已经使用SetHandlerLifetime(TimeSpan.FromSeconds(5)); 对其进行了测试,即使在收到无数请求 2 分钟后,httpClient 仍在继续工作,这是一个好兆头吗?

这是否意味着我不必担心 DNS 更改/套接字耗尽?

ConfigureServices方法内部:

var myOptions = app.ApplicationServices.GetService<IOptionsMonitor<MyOptions>>();
HttpClient httpClient = app.ApplicationServices.GetService<IHttpClientFactory>().CreateClient();
MyStaticObject.Configure(myOptions, httpClient);

编辑:添加了一些示例代码。

【问题讨论】:

  • 你在哪里实例化你的 HttpClient?如果您使用的是 HttpClientFactory ,它将为您处理生命周期。请参阅文档here
  • @TylerHundley 在 ConfigureServices 中,我正在创建一个默认的 HttpClient(未键入,未命名,只需直接使用 HttpClientFactory.CreateClient 方法,无需任何附加参数。
  • @TylerHundley 我在那里创建的实例,我传递给一个静态字段,它将在应用程序的剩余生命周期中继续存在。这样可以吗?据我了解,HttpMessageHandlers 确实每 2 分钟回收一次,有新的,避免了以前在单例 HttpClients 中发现的问题?
  • 你在哪里使用HttpClientFactory.CreateClient?你在使用依赖注入吗?你能添加一些你在哪里声明/使用你的客户端的示例代码吗?
  • @TylerHundley 完成!感谢您到目前为止提供的帮助。

标签: c# asp.net-core httpclient object-lifetime httpclientfactory


【解决方案1】:

这里有几件事要看,第一是MyStaticObject 真的需要是静态的吗?如果是这样,我建议将其注册为单例,以便您仍然可以利用依赖注入。完成此操作后,您可以注册 IHttpClientFactory 并从您的代码中使用它。您的 ConfigureServices 方法可能最终看起来像这样

public void ConfigureServices(IServiceCollection services)
{
  //The base extension method registers IHttpClientFactory
  services.AddHttpClient();
  services.AddSingleton<IMySingletonObject, MySingletonObject>();
}

然后在你的消费类中,MySingletonObject 在这种情况下,你可以这样配置它

public class MySingletonObject
{
  private readonly IHttpClientFactory _clientFactory;

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

  public async Task SomeMethodThatUsesTheClient()
  {
    var client = _clientFactory.CreateClient();
    //use the client
  }
}

原因是IHttpClientFactory 为我们处理了生命周期和池问题。根据docs

管理底层 HttpClientMessageHandler 实例的池化和生命周期。自动管理避免了手动管理 HttpClient 生命周期时出现的常见 DNS(域名系统)问题。

当您调用 CreateClient 时会发生这种情况,因此您希望使用客户端在代码中执行此操作,而不是在应用程序启动时执行此操作。

附带说明,如果您根本不需要这个类是单例,您可以使用扩展 services.AddHttpClient&lt;IMyClass, MyClass&gt;() 并将 HttpClient 直接注入到类中。 DI 容器将在幕后为您处理从工厂获取客户端。

【讨论】:

  • 我无法详细说明,但是静态类是一个属性,当它被用作属性而不是服务时,允许在其中进行依赖注入是非常错误的.因此,为什么我通过上述方式做到了。如果我跳过所有这些,并坚持我自己的版本,那会涵盖我对 HttpClient 的所有担忧和问题吗?我不明白为什么不这样做,因为您只是从 DI 传递一个 HttpClient,而不是直接从 HttpClientFactory 获取它(这被证明是一个有效的选项)
  • 当你传入一个 HttpClient 时,你会将其传递给范围内的类,因此每次都会传递它。在 Singleton 实例中,您传递整个工厂,然后在需要时使用 CreateClient 获取客户端。如果你必须保持静态,我会通过 Configure 方法传入整个 HttpClientFactory 并使用它,如我上面的示例所示
  • 我不相信你所说的完全正确,因为它没有任何意义。为什么我需要在每次请求后创建多个 httpClients?这不违反 httpClientFactory 的全部内容吗?此外,HttpClientFactory 的替代品SocketsHttpHandler 可以用两行代码解决我的问题。而且我 100% 确定以这种方式创建的 httpClient 确实会回收 httpMessageHandlers 并刷新 DNS 缓存。为什么同样的事情不适用于httpClientFactory 创建的实例?
  • 我的示例代码直接取自 Microsoft 的 HttpClientFactory here 使用示例。您可以通过 HttpClient here 查看潜在问题。请注意,您不会在每个请求中多次创建客户端,而只是在需要时创建一次。 (1/2)
  • HttpClientFactory 缓存了底层的HttpMessageHandler,这通常是套接字耗尽问题的来源。 (2/2)
【解决方案2】:
     public static IServiceCollection AddApiClient(this IServiceCollection services,
        Action<RgCommunicationClientOptions> action)
    {
        services.AddHttpClient<ISomeApiClient, SomeApiClient>()
            .AddPolicyHandler(GetRetryPolicy())
            .SetHandlerLifetime(TimeSpan.FromMinutes(4));
        services.AddOptions();
        services.Configure(action);

        return services;
    }
    
    static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
    {
        return HttpPolicyExtensions
            .HandleTransientHttpError()
            //.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
            .WaitAndRetryAsync(2, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
    }

 services.AddApiClient(options => options.BaseAddress = configuration.GetSection("ExternalServices:SomeApi")["Url"]);
       

你可以像上面那样通过依赖注入在类中注入 HttpClient。我已经使用 Poly 扩展来添加 RetryPolicy。您还可以使用 SetHaandlerLifeTime 设置处理程序的 LifetTime。这样您就可以为每个客户端单独配置客户端。在日志中,您可以看到 httpHandler 在相应调用的 4 分钟后过期。我已经使用扩展方法来传递方法中的选项,通过应用程序设置获取值。 希望这可以帮助。 块引用

【讨论】:

    猜你喜欢
    • 2018-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多