.NET应用调用外部API运行一段时间后连接关闭如何解决?
问题根因分析
该报错为.NET Framework下高频调用第三方API的典型问题,常见诱因如下:
- 端口耗尽:每次请求都新建
HttpClient/HttpWebRequest实例,导致本地临时端口被大量占用,处于TIME_WAIT状态的端口无法及时释放,超过阈值后新请求无法建立连接 - 服务端限流:短时间内大量请求发送至同一API,服务端触发频率限制主动断开连接
- 协议配置错误:未显式指定兼容的TLS版本,API服务端淘汰旧TLS版本后中途连接被拒绝
- 连接存活配置不当:未开启TCP保活,服务端主动关闭空闲连接后,后续请求复用已关闭连接触发报错
修复方案
1. 全局复用HttpClient实例(最高优先级)
.NET Framework下HttpClient设计为单例使用,每次新建实例会占用独立端口,需全局仅初始化一次:
// 全局静态初始化,整个应用生命周期只创建一次 private static readonly HttpClient _httpClient = new HttpClient(new HttpClientHandler { KeepAlive = true, MaxConnectionsPerServer = 10, // 可根据服务端耐受度调整,不要超过服务端并发限制 }) { Timeout = TimeSpan.FromSeconds(30) }; // 程序入口处配置全局连接规则 static void Main(string[] args) { // 显式指定TLS版本,*如果你的.NET Framework版本低于4.7.2不支持Tls13,仅保留Tls12即可 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13; ServicePointManager.DefaultConnectionLimit = 10; // 和上面MaxConnectionsPerServer保持一致 ServicePointManager.SetTcpKeepAlive(true, 30000, 10000); // 开启TCP保活 // 后续业务逻辑 }
注意不要在每次请求时都新建HttpClient实例,避免端口泄漏
2. 添加限流与重试机制
针对服务端限流场景,控制请求频率,同时添加重试逻辑应对偶发断连:
// 全局限制最大并发请求数为5,避免并发过高触发服务端限流 private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(5, 5); // 通用API请求方法 public async Task<string> CallApiAsync(string apiUrl) { await _semaphore.WaitAsync(); try { int retryCount = 0; // 连接异常最多重试3次 while (retryCount < 3) { try { var response = await _httpClient.GetAsync(apiUrl); response.EnsureSuccessStatusCode(); return await response.Content.ReadAsStringAsync(); } catch (HttpRequestException ex) when (ex.Message.Contains("The underlying connection was closed")) { retryCount++; if (retryCount == 3) throw; // 指数退避等待,每次重试等待时间翻倍 await Task.Delay(1000 * (int)Math.Pow(2, retryCount)); } } return null; } finally { _semaphore.Release(); // 每次请求后固定等待200ms,降低请求频率,可根据服务端限流规则调整 await Task.Delay(200); } }
3. 旧HttpWebRequest适配方案
如果你仍在使用HttpWebRequest而非HttpClient,添加以下配置:
var request = (HttpWebRequest)WebRequest.Create(apiUrl); request.KeepAlive = true; request.ProtocolVersion = HttpVersion.Version11; request.ServicePoint.ConnectionLimit = 10; request.ServicePoint.Expect100Continue = false;
4. 分批增量处理
不要一次性处理全部3500条记录,每处理200条记录后暂停10~30秒,释放闲置连接后再处理后续批次。
内容的提问来源于stack exchange,提问作者Dharmendra Kumar
相关产品推荐
相关产品推荐

