mar*_*dok 7 c# httpclient dotnet-httpclient .net-core
我们可以在这里阅读到您使用的是HTTPCLIENT WRONG,它正在破坏您的软件,我们不应该为每个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);
}
Run Code Online (Sandbox Code Playgroud)
它说每个调用将始终创建一个HttpClient实例,调用者可以对其进行缓存。
确保每次对IHttpClientFactory.CreateClient的调用都返回一个新的HttpClient实例。调用方可以无限期缓存返回的实例,也可以在需要时将其使用包含在using块中以将其处置。
所以问题是我应该完全依靠HttpClientFactory还是应该仍然缓存从中创建的HttpClient?
在我们的项目中,每次发出请求时,我们都使用HttpClientFactory.CreateClient,他仍然会有套接字异常。
HttpClient只是IDisposable因为它HttpMessageHandler是IDisposable。实际上,HttpMessageHandler这应该是长期存在的。
HttpClientFactory通过保持HttpMessageHandler内部长期存在来工作。每当您请求a时HttpClient,它都会使用寿命长的HttpMessageHander,并在处置a时告诉其HttpClient 不要对其进行处置HttpClient。
您可以在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;
}
Run Code Online (Sandbox Code Playgroud)
因此,从技术上讲,您是否缓存HttpClient或立即处置它都没有关系-处置它不会做任何事情(因为已告知不要处置HttpClientHandler,因为这是由进行管理的HttpClientFactory)。
关于配置HttpClient,MSDN说:
不需要处理客户端。Disposal取消传出的请求,并确保在调用Dispose之后不能使用给定的HttpClient实例。IHttpClientFactory跟踪并处置HttpClient实例使用的资源。HttpClient实例通常可以视为不需要处理的.NET对象。
在IHttpClientFactory诞生之前,通常使用一种模式来使单个HttpClient实例长期存活。迁移到IHttpClientFactory后,此模式就不再需要。
我怀疑SocketException您看到的原因有不同。也许问一个针对他们的新问题?
在 ASP.NET Core 2.2 版本中,情况发生了很大的变化。HttpClient预计消耗的方式仅通过 DI,它在内部为您使用 .DI 处理所有必要的缓存HttpClientFactory。以下文档文章已更新以反映这些新用例:https://learn.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();
Run Code Online (Sandbox Code Playgroud)
注意:有多种使用模式,这是最基本的一种。查看文档中的其他模式,这可能更适合您的需求。
稍后,在课程中,您想要从何处发出 http 请求,获取依赖项IHttpClientFactory并让 DI 根据需要为您实例化它。以下是来自 Microsoft 文档的示例:
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>();
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1153 次 |
| 最近记录: |