內測階段發現服務器連接數異常增長,netstat -an | grep CLOSE_WAIT 顯示大量 CLOSE_WAIT 狀態連接未釋放,隨時間增長最終導致文件描述符耗盡、服務不可用。
CLOSE_WAIT 表示**被動關閉方**(服務端)已收到 FIN 並發送 ACK,但**應用層未調用 close() 釋放套接字**。Java 中常見於 HTTP 客戶端調用外部 API 後,未正確釋放連接資源。
排查發現調用短信供應商 HTTP 接口的工具類使用了 Apache HttpClient 3.x,但缺釋放連接邏輯:
// 問題代碼
HttpClient client = new HttpClient();
PostMethod method = new PostMethod(url);
// ... 設置參數、執行請求
client.executeMethod(method);
// 缺少釋放連接!
在 finally 塊中確保釋放連接並關閉閒置連接管理器:
HttpClient client = new HttpClient();
PostMethod method = null;
try {
method = new PostMethod(url);
// 設置參數...
client.executeMethod(method);
// 處理響應...
} catch (Exception e) {
log.error("HTTP 調用失敗", e);
throw e;
} finally {
if (method != null) {
method.releaseConnection(); // 關鍵:歸還連接到管理器
}
// 關鍵:強制關閉閒置連接,防洩漏
client.getHttpConnectionManager().closeIdleConnections(0);
}
PoolingHttpClientConnectionManager 統一管理連接生命周期,配合 CloseableHttpClient。try (CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(poolingConnManager)
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectTimeout(3000)
.setSocketTimeout(5000)
.setConnectionRequestTimeout(1000)
.build())
.build()) {
HttpPost post = new HttpPost(url);
post.setEntity(new UrlEncodedFormEntity(params, StandardCharsets.UTF_8));
try (CloseableHttpResponse response = httpClient.execute(post)) {
// 處理 response.getEntity()...
EntityUtils.consumeQuietly(response.getEntity()); // 確保流讀完歸還連接
}
} // 自動關閉 client,歸還連接
修復上線後,CLOSE_WAIT 數量歸零,連接池穩定複用,上線風險徹底消除。記住:**每一次 HTTP 調用,都要對連接的歸還負責。**