由 HttpClientFactory 创建的 HttpClient 实例是不是应该被释放?

Posted

技术标签:

【中文标题】由 HttpClientFactory 创建的 HttpClient 实例是不是应该被释放?【英文标题】:Should HttpClient instances created by HttpClientFactory be disposed?由 HttpClientFactory 创建的 HttpClient 实例是否应该被释放? 【发布时间】:2018-11-27 11:00:24 【问题描述】:

所以,我在我的 Startup.cs 中使用服务集合注册了一个命名客户端:

services.AddHttpClient(someServiceName, 
                       client => client.BaseAddress = baseAddress);

现在可以从我的服务提供商那里注入一个IHttpClientFactory

使用这个IHttpClientFactory,我想出了一个客户端实例:

var client = httpClientFactory.CreateClient(someServiceName)

曾几何时,在处理HttpClient 实例时必须非常小心,因为它是rarely the right thing to do。

但是,现在我们有了HttpClientFactory,这还重要吗?这个client 是否应该/可以无忧处理?例如

using (var httpClient = httpClientFactory.CreateClient(someServiceName))
using (var response = await httpClient.PostAsync(somePath, someData))

    var content = await response.Content.ReadAsAsync<SomeResponse>();
    //...

【问题讨论】:

查看这篇文章,它解释了工厂如何处理客户stevejgordon.co.uk/introduction-to-httpclientfactory-aspnetcore @Nkosi 是的,我读过。但是,我找到了一些看起来很像我的问题中最后一个 sn-p 的代码,我想知道是否需要修复它。 【参考方案1】:

没有。您不应该处置您的客户。更一般地说,您不应该处理通过 DI 容器检索到的 anything,在 ASP.NET Core 中,默认情况下该容器是服务集合。生命周期由 DI 容器管理,因此如果您处置客户端,但稍后将其注入某些东西,您将获得 ObjectDisposedException。让容器处理处置。

这实际上是与IDisposable 类的常见混淆。如果您的类本身拥有依赖项,则您应该亲自实现IDisposable。如果它的所有依赖项都注入,则不应实现IDisposable,因为它不拥有任何需要处理的东西。同样,您不应该处理注入到您的类中的任何东西,因为它不拥有这些依赖项。只处理你特别新的东西。如果您没有看到关键字 new,您可能不应该丢弃。

【讨论】:

这有点令人困惑,因为客户端不是通过 DI 容器检索的。 factory 是通过 DI 容器检索的。 但是 httpClientFactory.CreateClient(someServiceName) 中的命名似乎暗示你是新的,所以这是令人困惑的部分。我和@KierenJohnstone 在一起 资源是否被更新并不重要,重要的是你是否控制它的生命周期。在工厂的情况下,工厂进行实例化并控制生命周期,而不是您。这才是重点。只处置属于您的资源。 @ChrisPratt 如果 C# 或 .NET 包含某种对象生命周期和所有权语义以防止这种混淆,您是否同意这不会成为问题?甚至是简单的东西,例如接口(例如IDisposableHahahNotReally)或通过智能指针类型对象引用对象,例如OwnedObject&lt;T&gt;.InstanceBorrowedObject&lt;T&gt;.Value)。 @ChrisPratt 我认为这并不重要。我记得在 ASP.NET 存储库上的 GitHub 问题中读到它是 HttpClientHandler 不能被释放。当您处理 HttpClient(由上述工厂创建)时,处理程序“告诉”客户端在处理它时不要处理它。所以,像这样的代码完全没有问题using (var client = _httpClientFactory.CreateClient())。 (我认为):)【参考方案2】:

不需要调用Dispose 方法,但如果出于某些原因需要,您仍然可以调用它。

证明:HttpClient and lifetime management

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

查看source of DefaultHttpClientFactory

public HttpClient CreateClient(string name)

    if (name == null)
    
        throw new ArgumentNullException(nameof(name));
    

    var handler = CreateHandler(name);
    var client = new HttpClient(handler, disposeHandler: false);

    var options = _optionsMonitor.Get(name);
    for (var i = 0; i < options.HttpClientActions.Count; i++)
    
        options.HttpClientActions[i](client);
    

    return client;

HttpMessageHandler 的实例存储了HttpClient 的非托管资源。在经典场景中,HttpClient 创建HttpMessageHandler 的实例,并在自己处理的同时处理它。

您可以在上面的代码中看到HttpClient 的不同实例共享HttpMessageHandler 的单个实例并且不释放它(disposeHandler: false)。

所以,HttpClient.Dispose 的调用没有任何作用。但这并不危险。

【讨论】:

以上是关于由 HttpClientFactory 创建的 HttpClient 实例是不是应该被释放?的主要内容,如果未能解决你的问题,请参考以下文章

.NET Core HttpClientFactory+Consul实现服务发现

如何在 .NET Core 中使用 HttpClientHandler 和 HttpClientFactory

如何使用 Moq 在 .NET Core 2.1 中模拟新的 HttpClientFactory

asp.net core 使用HttpClientFactory Polly实现熔断降级

第二十六节:扩展如何在控制台中使用HttpClientFactory读取配置文件数据保护注入类

用HttpClientFactory来实现简单的熔断降级