.NET 6 Windows服务运行24小时后HttpClient调用持续超时求助
.NET 6 Windows服务24小时后API调用超时问题排查建议
问题背景
.NET 6.0 Windows服务每隔几秒向API发送数据,运行约24小时后持续出现超时异常,仅能通过重启服务解决。已配置PooledConnectionLifetime为2分钟、自定义超时,三个API各有独立静态HttpClient实例。
现有API调用代码
private static HttpClient client = new HttpClient(); public static void SetTimeout(int timeout, int poolConnectionLifeTime) { var handler = new SocketsHttpHandler { PooledConnectionLifetime = TimeSpan.FromMinutes(poolConnectionLifeTime) }; client = new HttpClient(handler); client.Timeout = TimeSpan.FromSeconds(timeout); TokenRetriever.Log.Info("Auth API http client initialised"); } public async Task<string> ExecuteGetAndReturnResponse(Dictionary<string, string> requestParams, string url) { string responseReturned = string.Empty; try { using (var response = await client.PostAsync(url, new FormUrlEncodedContent(requestParams))) { if (response.IsSuccessStatusCode) { using (var stream = await response.Content.ReadAsStreamAsync()) { using (var streamReader = new StreamReader(stream)) { responseReturned = await streamReader.ReadToEndAsync(); } } } else { responseReturned = $"ERROR: - {response.StatusCode} - {response.ReasonPhrase}"; TokenRetriever.Log.Info($"Response returned {responseReturned}"); } } } catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException) { TokenRetriever.Log.Error("Fetching token Timed out: " + ex.Message); } catch (TaskCanceledException ex) { // Handle cancellation. TokenRetriever.Log.Error("Task Cancellation fetching token error: " + ex.Message); } return responseReturned; }
解决方案建议
- 修复静态HttpClient初始化的线程安全问题:当前
SetTimeout直接替换静态client实例,多线程场景下可能出现竞态条件,导致部分请求使用未正确配置的旧实例。建议用Lazy<HttpClient>实现线程安全的惰性初始化:private static readonly Lazy<HttpClient> _client = new Lazy<HttpClient>(() => { var handler = new SocketsHttpHandler { PooledConnectionLifetime = TimeSpan.FromMinutes(2) }; var client = new HttpClient(handler); client.Timeout = TimeSpan.FromSeconds(30); TokenRetriever.Log.Info("Auth API http client initialised"); return client; }); private static HttpClient Client => _client.Value; - 补充连接池相关配置:除
PooledConnectionLifetime外,设置PooledConnectionIdleTimeout(如1分钟)避免闲置连接长期占用;调整MaxConnectionsPerServer为合理值,防止连接池耗尽。 - 添加重试机制:针对超时、服务端错误实现指数退避重试,可使用Polly库简化逻辑,避免单次超时引发持续失败:
var retryPolicy = Policy .Handle<TaskCanceledException>(ex => ex.InnerException is TimeoutException) .Or<HttpRequestException>() .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); var response = await retryPolicy.ExecuteAsync(async () => await Client.PostAsync(url, new FormUrlEncodedContent(requestParams))); - 处理DNS缓存问题:.NET HttpClient默认缓存DNS解析结果,若API IP变更会导致旧IP失效。在
SocketsHttpHandler中配置DnsRefreshTimeout(如5分钟)强制刷新DNS:var handler = new SocketsHttpHandler { PooledConnectionLifetime = TimeSpan.FromMinutes(2), DnsRefreshTimeout = TimeSpan.FromMinutes(5) }; - 增强日志维度:记录请求ID、连接耗时、活跃连接数,超时异常时补充请求上下文信息,帮助定位是连接池、API端还是网络问题。
- 改用IHttpClientFactory管理实例:依赖注入方式的
IHttpClientFactory会自动处理连接池和实例生命周期,避免手动管理静态HttpClient的潜在问题,更适合长期运行的服务场景。
内容的提问来源于stack exchange,提问作者AndrewE
相关产品推荐
相关产品推荐

