You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

IHttpClientFactory配置HttpClientHandler及Socket异常问题咨询

关于IHttpClientFactory的Socket异常与Handler复用问题

问题背景

使用基于IHttpClientFactory依赖注入实现的HttpClient时,频繁遇到间歇性Socket异常。根据微软文档说明:每次调用IHttpClientFactory的CreateClient都会返回新的HttpClient实例,每个客户端名称对应一个HttpClientHandler实例,由工厂管理其生命周期。

疑问:下述命名客户端的DI配置,是否每次调用CreateClient都会创建新的HttpClientHandler,导致无法利用连接池的优势?

Startup.cs配置代码

services.AddHttpClient("HttpClientFactory", client =>
            {
                client.Timeout = TimeSpan.FromSeconds(30);
            })
              .ConfigurePrimaryHttpMessageHandler(() =>
              {
                  var handler = new HttpClientHandler();
                  handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
                  handler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11;
                  handler.UseProxy = false;
                  return handler;
              });

使用代码

HttpMethod = httpMethodSelection(RequestType);
request = new HttpRequestMessage(HttpMethod, BaseUrl);
request.Headers.Add("Accept-Encoding", "gzip, deflate");
if (!string.IsNullOrWhiteSpace(AccessToken))
{
    request.Headers.Add("Authorization", "Bearer " + AccessToken);
}
if (HttpMethod != HttpMethod.Get)
{
    request.Content = new StringContent(JsonRequest, Encoding.UTF8, "application/json");
}

ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12 | System.Net.SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls;
var _httpClient = _httpClientFactory.CreateClient("HttpClientFactory");
response = await _httpClient.SendAsync(request);

rawResponse = await response?.Content?.ReadAsStringAsync();

频繁出现的错误信息

错误1

System.Threading.Tasks.TaskCanceledException: The request was canceled due to the configured HttpClient.Timeout of 30 seconds elapsing.
 ---> System.TimeoutException: The operation was canceled. 
 ---> System.Threading.Tasks.TaskCanceledException: The operation was canceled.
 ---> System.ObjectDisposedException: Cannot access a disposed object.Object name: 'SslStream'.   at System.Net.Security.SslStream.<ThrowIfExceptional>g__ThrowExceptional|138_0(ExceptionDispatchInfo e)   at System.Net.Security.SslStream.WriteAsync(ReadOnlyMemory`1 buffer, CancellationToken cancellationToken)   at System.Net.Http.HttpConnection.WriteToStreamAsync(ReadOnlyMemory`1 source, Boolean async)  at System.Net.Http.HttpConnection.FlushAsync(Boolean async)   at System.Net.Http.HttpConnection.SendRequestContentAsync(HttpRequestMessage request, HttpContentWriteStream stream, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---  at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)  at System.Net.Http.DiagnosticsHandler.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.DecompressionHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)   at Microsoft.Extensions.Http.Logging.LoggingHttpMessageHandler.<SendCoreAsync>g__Core|5_0(HttpRequestMessage request, Boolean useAsync, CancellationToken cancellationToken)   at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.<SendCoreAsync>g__Core|5_0(HttpRequestMessage request, Boolean useAsync, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
   --- End of inner exception stack trace ---  --- End of inner exception stack trace ---   at System.Net.Http.HttpClient.HandleFailure(Exception e, Boolean telemetryStarted, HttpResponseMessage response, CancellationTokenSource cts, CancellationToken cancellationToken, CancellationTokenSource pendingRequestsCts)
   at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)   at AirlineConnector.JazeeraAir.Helpers.Helper.ProviderLogonCallAsync(String url, String Request, HttpMethod httpMethod, String TokenRes, Boolean IsHold, String CallerMemberName, String CallerFilePath) in /src/AirlineConnector.JazeeraAir/Helpers/Helper.cs:line 183

错误2

System.Threading.Tasks.TaskCanceledException: The request was canceled due to the configured HttpClient.Timeout of 30 seconds elapsing.
 ---> System.TimeoutException: The operation was canceled.
 ---> System.Threading.Tasks.TaskCanceledException: The operation was canceled.
 ---> System.IO.IOException: Unable to read data from the transport connection: Operation canceled.
 ---> System.Net.Sockets.SocketException (125): Operation canceled
   --- End of inner exception stack trace ---
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken)
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource<System.Int32>.GetResult(Int16 token)
   at System.Net.Security.SslStream.EnsureFullTlsFrameAsync[TIOAdapter](TIOAdapter adapter)
   at System.Net.Security.SslStream.ReadAsyncInternal[TIOAdapter](TIOAdapter adapter, Memory`1 buffer)
   at System.Net.Http.HttpConnection.InitialFillAsync(Boolean async)
   at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.DiagnosticsHandler.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.DecompressionHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at Microsoft.Extensions.Http.Logging.LoggingHttpMessageHandler.<SendCoreAsync>g__Core|5_0(HttpRequestMessage request, Boolean useAsync, CancellationToken cancellationToken)
   at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.<SendCoreAsync>g__Core|5_0(HttpRequestMessage request, Boolean useAsync, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
   --- End of inner exception stack trace ---
   --- End of inner exception stack trace ---
   at System.Net.Http.HttpClient.HandleFailure(Exception e, Boolean telemetryStarted, HttpResponseMessage response, CancellationTokenSource cts, CancellationToken cancellationToken, CancellationTokenSource pendingRequestsCts)
   at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)   at AirlineConnector.JazeeraAir.Helpers.Helper.ProviderLogonCallAsync(String url, String Request, HttpMethod httpMethod, String TokenRes, Boolean IsHold, String CallerMemberName, String CallerFilePath) in /src/AirlineConnector.JazeeraAir/Helpers/Helper.cs:line 183

解答与解决方案

核心疑问解答

你的配置不会每次调用CreateClient都创建新的HttpClientHandler。IHttpClientFactory对命名客户端的Handler做复用管理:通过ConfigurePrimaryHttpMessageHandler注册的工厂方法,只会在首次创建该命名客户端的Handler实例时执行一次,后续该命名客户端的所有HttpClient实例都会复用同一个Handler池(工厂会自动管理Handler的生命周期,定期回收过期实例)。所以你的配置本身符合连接池复用逻辑,不是导致问题的直接原因。

异常原因分析

从错误栈来看,核心是请求超时后触发的Socket连接/SSL流操作异常,可能的原因:

  • 连接池耗尽:默认ServicePointManager.DefaultConnectionLimit为2,短时间大量请求会导致排队等待连接,最终超时。
  • 超时配置冲突:全局30秒超时未结合请求级取消令牌,部分请求因等待连接或服务响应缓慢触发超时。
  • 资源未正确释放:HttpRequestMessage/HttpResponseMessage未用using包裹,导致连接无法及时回池。
  • TLS配置冲突:同时在请求代码和Handler中设置TLS版本,可能引发SSL握手异常。

具体修复方案

  1. 调整连接池限制
    在应用启动时(如Startup.cs的ConfigureServices开头)设置一次全局连接数:
ServicePointManager.DefaultConnectionLimit = 100; // 根据并发量调整

移除请求代码中的ServicePointManager.SecurityProtocol设置,避免全局配置重复修改。

  1. 优化超时与取消令牌
    给单个请求设置独立取消令牌,避免全局超时影响所有请求:
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
using var response = await _httpClient.SendAsync(request, cts.Token);
  1. 确保资源正确释放
    用using包裹HttpRequestMessage和HttpResponseMessage,保证资源及时回收:
using var request = new HttpRequestMessage(HttpMethod, BaseUrl);
// 配置请求头和内容...
using var response = await _httpClient.SendAsync(request);
rawResponse = await response.Content.ReadAsStringAsync();
  1. 统一TLS配置
    仅在Handler中设置TLS版本,保持配置一致性:
.ConfigurePrimaryHttpMessageHandler(() =>
{
    var handler = new HttpClientHandler();
    handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
    handler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11;
    handler.UseProxy = false;
    return handler;
});
  1. 添加重试策略
    使用Polly扩展给HttpClient添加重试,处理间歇性连接异常:
services.AddHttpClient("HttpClientFactory", client =>
{
    client.Timeout = TimeSpan.FromSeconds(30);
})
.ConfigurePrimaryHttpMessageHandler(() =>
{
    var handler = new HttpClientHandler();
    handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
    handler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11;
    handler.UseProxy = false;
    return handler;
})
.AddPolicyHandler(Policy
    .Handle<HttpRequestException>()
    .Or<TaskCanceledException>()
    .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))));

内容的提问来源于stack exchange,提问作者Nishad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 03:24:55