CloseableHttpClient 實例可共享,內部連接池管理同步。
// 默認配置:每路由 2 條,總上限 20 條 —— 生產環境通常不夠
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(200); // 總連接數上限
cm.setDefaultMaxPerRoute(50); // 每路由 (host:port) 上限
// 針對特定路由調整
cm.setMaxPerRoute(new HttpRoute(new HttpHost("api.example.com")), 100);
HttpClient 遵循 HTTP 規範:
Keep-Alive: timeout=30,若存在則使用。
ConnectionKeepAliveStrategy strategy = (response, context) -> {
// 1. 嘗試讀取服務端返回的 Keep-Alive 頭
HeaderElementIterator it = new BasicHeaderElementIterator(
response.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
HeaderElement he = it.nextElement();
if ("timeout".equalsIgnoreCase(he.getName())) {
try { return Long.parseLong(he.getValue()) * 1000; }
catch (NumberFormatException ignore) {}
}
}
// 2. 針對特定惡性服務端強制縮短
HttpHost target = (HttpHost) context.getAttribute(HttpClientContext.HTTP_TARGET_HOST);
if ("bad-server.com".equalsIgnoreCase(target.getHostName())) return 5_000; // 5秒
// 3. 默認保守值
return 30_000; // 30秒
};
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(cm)
.setKeepAliveStrategy(strategy)
.build();
服務端主動關閉連接時,客戶端無法感知 (TCP 半開狀態)。必須啟動監控線程定期掃描連接池。
public class IdleConnectionMonitorThread extends Thread {
private final HttpClientConnectionManager connMgr;
private volatile boolean shutdown;
public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr) {
this.connMgr = connMgr;
}
@Override
public void run() {
try {
while (!shutdown) {
synchronized (this) { wait(5000); } // 每 5 秒掃描一次
// 關閉過期連接 (服務端已斷開但客戶端不知)
connMgr.closeExpiredConnections();
// 關閉空閒超過 30 秒的連接 (防止佔用資源)
connMgr.closeIdleConnections(30, TimeUnit.SECONDS);
}
} catch (InterruptedException ex) { /* 線程結束 */ }
}
public void shutdown() {
shutdown = true;
synchronized (this) { notifyAll(); }
}
}
// 啟動方式
IdleConnectionMonitorThread monitor = new IdleConnectionMonitorThread(cm);
monitor.setDaemon(true); // 隨 JVM 退出
monitor.start();
// 應用關閉時調用 monitor.shutdown()
| 參數 | 建議值 | 說明 |
|---|---|---|
| MaxTotal | 200-500 | 根據並發量與下游服務承受力設定 |
| DefaultMaxPerRoute | 50-100 | 防止單一服務佔滿連接池 |
| ConnectionRequestTimeout | 1-5s | 從池獲取連接超時,防止線程無限等待 |
| ConnectTimeout | 5-10s | TCP 握手超時 |
| SocketTimeout | 15-60s | 讀取數據超時 (業務 SLA 決定) |
| Keep-Alive Strategy | 自定義 30s 默認 | 覆蓋無限存活假設 |
| Idle Monitor | 5s 間隔 / 30s 空閒 | 必須啟動,防洩漏 |
| Stale Connection Check | 啟用 | 執行請求前輕量檢測連接有效性 |
RequestConfig config = RequestConfig.custom()
.setConnectionRequestTimeout(3000)
.setConnectTimeout(5000)
.setSocketTimeout(30000)
.setStaleConnectionCheckEnabled(true)
.build();
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cm)
.setDefaultRequestConfig(config)
.setKeepAliveStrategy(strategy)
.evictIdleConnections(30, TimeUnit.SECONDS) // 4.5+ 內置支持
.build();
HttpClient 高效穩定的關鍵在於:合理設定連接池上限、強制保守的 Keep-Alive 策略、必須啟動空閒連接清理監控線程、啟用過期連接檢測。將連接池視為有限資源池精細管理,而非「用完即丟」的無限資源。