.NET Core中使用HttpClient/HttpClientFactory遇异常求助
异常详情
System.IO.IOException: 无法从传输连接读取数据: I/O操作因线程退出或应用请求而中止。
System.Net.Sockets.SocketException (995): I/O操作因线程退出或应用请求而中止。
System.Net.Http.HttpRequestException: 发送请求时出错。 ---> System.IO.IOException: 响应提前结束。
当前配置与代码
Startup.cs
builder.Services .AddHttpClient<IRequestHandler, RequestHandler>() .SetHandlerLifetime(TimeSpan.FromMinutes(5)) .ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler { SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13 }) .AddPolicyHandler(retryPolicyHttp); services.AddTransient<IRequestHandler, RequestHandler>();
RequestHandler.cs
public class RequestHandler: IRequestHandler { private readonly IHttpClientFactory _httpClientFactory; public RequestHandler(IHttpClientFactory httpClientFactory) { _httpClientFactory = httpClientFactory; } public virtual HttpResponseMessage ExecuteThirdPartyServices(HttpMethod httpMethod, string postData, RequestModel requestModel) { using HttpRequestMessage request = new(httpMethod, requestModel.RequestUri); HttpResponseMessage httpMessageResponse; using (var httpClient = _httpClientFactory.CreateClient()) { httpClient.Timeout = TimeSpan.FromMinutes(5); httpMessageResponse = httpClient.SendAsync(request).Result; } return httpMessageResponse; } }
修复建议
1. 替换同步阻塞为异步调用
使用.Result强制同步等待异步操作会破坏.NET异步上下文,容易引发线程池饥饿、死锁,最终导致线程中止类异常。改为全异步实现:
public virtual async Task<HttpResponseMessage> ExecuteThirdPartyServices(HttpMethod httpMethod, string postData, RequestModel requestModel) { using HttpRequestMessage request = new(httpMethod, requestModel.RequestUri); // 针对POST/PUT等方法添加请求体 if (!string.IsNullOrWhiteSpace(postData)) { request.Content = new StringContent(postData, Encoding.UTF8, "application/json"); // 可根据第三方服务要求调整Content-Type } // 获取配置好的命名客户端 using var httpClient = _httpClientFactory.CreateClient(nameof(RequestHandler)); httpClient.Timeout = TimeSpan.FromMinutes(5); // 可选:使用ResponseHeadersRead提前返回,减少连接占用时间 return await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); }
调用方需同步改为异步调用,保持异步链路完整。
2. 正确获取配置后的HttpClient
当前CreateClient()未指定名称,会创建默认客户端,而非你在AddHttpClient<IRequestHandler, RequestHandler>中配置的带有重试策略、SSL设置的客户端。需指定客户端名称(或直接使用服务类型名)来获取正确配置的实例。
3. 移除重复的服务注册
AddHttpClient<IRequestHandler, RequestHandler>已经自动注册了IRequestHandler为Transient服务,无需再单独调用services.AddTransient<IRequestHandler, RequestHandler>(),重复注册可能导致实例创建逻辑混乱。
4. 补充请求体内容
代码中接收了postData参数但未添加到请求中,如果是POST/PUT请求,空请求体可能导致第三方服务提前关闭连接,触发"响应提前结束"异常。需根据请求方法添加对应的请求内容。
5. 检查重试策略配置
确认retryPolicyHttp是否针对连接异常、IO异常设置了合理的重试逻辑,避免因无限制重试导致资源耗尽,或重试时机不当加剧线程问题。
内容的提问来源于stack exchange,提问作者XamDev

