我应该缓存并重用从 HttpClientFactory 创建的 HttpClient 吗?
Should I cache and reuse HttpClient created from HttpClientFactory?
我们可以在这里阅读YOU'RE USING HTTPCLIENT WRONG AND IT IS DESTABILIZING YOUR SOFTWARE that we should not create and dispose HttpClient for each http request. Instead, it should be cached and reused (e.g as Singleton in DI container). As well in official .NET documentation for HttpClient:
HttpClient 旨在实例化一次并在应用程序的整个生命周期中重复使用。为每个请求实例化一个 HttpClient class 将耗尽重负载下可用的套接字数量。这将导致 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 都保证 return 一个新的 HttpClient
实例。调用者可以无限期缓存 returned 实例或环绕
它在 using 块中的使用在需要时处理它。
所以问题是我应该完全依赖 HttpClientFactory 还是应该缓存从中创建的 HttpClient?
在我们的项目中,我们每次请求时都使用HttpClientFactory.CreateClient,他仍然会出现套接字异常。
HttpClient
只是 IDisposable
因为它的 HttpMessageHandler
是 IDisposable
。实际上,应该是长寿的HttpMessageHandler
。
HttpClientFactory
通过在内部保持长寿命的 HttpMessageHandler
来工作。每当您请求 HttpClient
时,它都会使用长期存在的 HttpMessageHander
,并告诉 HttpClient
not 在 [=11] 时处理它=] 已处理。
你可以看到 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
,因为这是由 HttpClientFactory
).
管理的
关于处理 HttpClient
、MSDN says:
Disposal of the client isn't required. Disposal cancels outgoing requests and guarantees the given HttpClient instance can't be used after calling Dispose. IHttpClientFactory tracks and disposes resources used by HttpClient instances. The HttpClient instances can generally be treated as .NET objects not requiring disposal.
Keeping a single HttpClient instance alive for a long duration is a common pattern used before the inception of IHttpClientFactory. This pattern becomes unnecessary after migrating to IHttpClientFactory.
我怀疑您看到的 SocketException
有不同的原因。也许提出一个针对他们的新问题?
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 核心团队的@RyanNowak 在以下 ASP.Net 核心社区站会 session 中介绍了所有这些更改:https://www.youtube.com/watch?v=Lb12ZtlyMPg
如果你还没有看过它,我强烈建议你观看它,因为它非常有用和有教育意义。
这是一个展示用法的小示例。在 Startup.ConfigureServices
方法调用中:
services.AddHttpClient();
注意:有多种使用方式,这是最基本的一种。在文档中查看其他模式,它们可能更适合您的需求。
稍后,在 class 中,您想从中发出 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>();
}
}
}
我们可以在这里阅读YOU'RE USING HTTPCLIENT WRONG AND IT IS DESTABILIZING YOUR SOFTWARE that we should not create and dispose HttpClient for each http request. Instead, it should be cached and reused (e.g as Singleton in DI container). As well in official .NET documentation for HttpClient:
HttpClient 旨在实例化一次并在应用程序的整个生命周期中重复使用。为每个请求实例化一个 HttpClient class 将耗尽重负载下可用的套接字数量。这将导致 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 都保证 return 一个新的 HttpClient 实例。调用者可以无限期缓存 returned 实例或环绕 它在 using 块中的使用在需要时处理它。
所以问题是我应该完全依赖 HttpClientFactory 还是应该缓存从中创建的 HttpClient?
在我们的项目中,我们每次请求时都使用HttpClientFactory.CreateClient,他仍然会出现套接字异常。
HttpClient
只是 IDisposable
因为它的 HttpMessageHandler
是 IDisposable
。实际上,应该是长寿的HttpMessageHandler
。
HttpClientFactory
通过在内部保持长寿命的 HttpMessageHandler
来工作。每当您请求 HttpClient
时,它都会使用长期存在的 HttpMessageHander
,并告诉 HttpClient
not 在 [=11] 时处理它=] 已处理。
你可以看到 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
,因为这是由 HttpClientFactory
).
关于处理 HttpClient
、MSDN says:
Disposal of the client isn't required. Disposal cancels outgoing requests and guarantees the given HttpClient instance can't be used after calling Dispose. IHttpClientFactory tracks and disposes resources used by HttpClient instances. The HttpClient instances can generally be treated as .NET objects not requiring disposal.
Keeping a single HttpClient instance alive for a long duration is a common pattern used before the inception of IHttpClientFactory. This pattern becomes unnecessary after migrating to IHttpClientFactory.
我怀疑您看到的 SocketException
有不同的原因。也许提出一个针对他们的新问题?
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 核心团队的@RyanNowak 在以下 ASP.Net 核心社区站会 session 中介绍了所有这些更改:https://www.youtube.com/watch?v=Lb12ZtlyMPg 如果你还没有看过它,我强烈建议你观看它,因为它非常有用和有教育意义。
这是一个展示用法的小示例。在 Startup.ConfigureServices
方法调用中:
services.AddHttpClient();
注意:有多种使用方式,这是最基本的一种。在文档中查看其他模式,它们可能更适合您的需求。
稍后,在 class 中,您想从中发出 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>();
}
}
}