Apache HttpClient (3.x/4.x/5.x) 中**三種超時設定**是生產環境穩定性的基石。配置錯誤會導致:連線耗盡、請求卡死、資源洩漏、熔斷失效。本文從**底層 Socket 行為、連線池邏輯、異常層次、版本差異**四個維度徹底講透,並給出生產級配置範本與排查清單。
| 超時類型 | HttpClient 4.x 設定 | HttpClient 5.x 設定 | 底層行為 | 拋出異常 |
|---|---|---|---|---|
| 連線池取連線超時 (Connection Request Timeout) | ConnManagerParams.setTimeoutRequestConfig.setConnectionRequestTimeout | RequestConfig.setConnectionRequestTimeout | 從 PoolingHttpClientConnectionManager 併發隊列取可用連線/等待新建連線 | ConnectionPoolTimeoutException (4.x)ConnectionRequestTimeoutException (5.x) |
| 建立連線超時 (Connect Timeout) | HttpConnectionParams.setConnectionTimeoutRequestConfig.setConnectTimeout | RequestConfig.setConnectTimeout | TCP 三次交握 + SSL/TLS 握手完成 | ConnectTimeoutException |
| 讀取資料超時 (Socket/Read Timeout) | HttpConnectionParams.setSoTimeoutRequestConfig.setSocketTimeout | RequestConfig.setResponseTimeout | Socket read() 等待資料封包到達 (含 HTTP 響應頭+體) | SocketTimeoutException (Read timed out) |
[Client 執行緒]
│
├─► 1. 向連線池請求連線 (ConnectionRequestTimeout)
│ └─ 池有空閒 → 直接返回
│ └─ 池滿/無空閒 → 等待/新建 (受 MaxTotal/MaxPerRoute 限制)
│
├─► 2. 建立 TCP 連線 (ConnectTimeout)
│ ├─ DNS 解析
│ ├─ SYN → SYN/ACK → ACK
│ └─ (HTTPS) TLS ClientHello → ServerHello → ... → Application Data
│
├─► 3. 發送 HTTP Request (通常無單獨超時,受 SocketTimeout 保護)
│
└─► 4. 讀取 HTTP Response (SocketTimeout/ResponseTimeout)
├─ 讀取 Status Line + Headers
└─ 讀取 Body (Chunked/Content-Length)
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(200); // 整體最大連線數
cm.setDefaultMaxPerRoute(50); // 每路由最大連線數
RequestConfig defaultConfig = RequestConfig.custom()
.setConnectionRequestTimeout(1000) // 從池取連線最長等 1s
.setConnectTimeout(3000) // TCP+TLS 握手最長 3s
.setSocketTimeout(10000) // 讀取響應最長 10s
.setStaleConnectionCheckEnabled(true) // 執行前檢查連線是否殘留
.build();
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(cm)
.setDefaultRequestConfig(defaultConfig)
.evictIdleConnections(30, TimeUnit.SECONDS) // 定期清理閒置連線
.evictExpiredConnections() // 清理過期連線
.build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(200);
cm.setDefaultMaxPerRoute(50);
RequestConfig config = RequestConfig.custom()
.setConnectionRequestTimeout(Timeout.ofSeconds(1))
.setConnectTimeout(Timeout.ofSeconds(3))
.setResponseTimeout(Timeout.ofSeconds(10)) // 5.x 統稱 ResponseTimeout
.build();
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(cm)
.setDefaultRequestConfig(config)
.evictIdleConnections(Duration.ofSeconds(30))
.build();
ConnectionPoolTimeoutException** | 1. 下游服務慢導致連線歸還慢MaxTotal 過小response.close() / EntityUtils.consume() 導致連線洩漏 | 1. 調大 MaxTotal/MaxPerRouteSocketTimeout 避免慢下游佔用ConnectTimeoutException 頻發** | 1. 網路不通/防火牆SocketTimeoutException: Read timed out** | 1. 下游處理超時setStaleConnectionCheckEnabled(true) |
| **連線洩漏導致應用掛起** | 未消費響應實體 / 未釋放連線 | **強制規範**:try (CloseableHttpResponse resp = client.execute(req)) { EntityUtils.consume(resp.getEntity()); } |
// 1. 一般 REST API (快速失敗、高併發)
RequestConfig apiConfig = RequestConfig.custom()
.setConnectionRequestTimeout(500)
.setConnectTimeout(2000)
.setSocketTimeout(5000)
.build();
// 2. 檔案上傳/下載 (長連線、大資料量)
RequestConfig fileConfig = RequestConfig.custom()
.setConnectionRequestTimeout(5000)
.setConnectTimeout(10000)
.setSocketTimeout(300000) // 5 分鐘
.build();
// 3. 串流/長輪詢 (SSE、WebSocket 升級前)
RequestConfig streamConfig = RequestConfig.custom()
.setConnectionRequestTimeout(1000)
.setConnectTimeout(5000)
.setSocketTimeout(0) // 0 表示無限等待 (需應用層心跳控制)
.build();
leased / maxTotal > 80% 需擴容或優化下游。ss -tan state time-wait 過多暗示短連線風暴。💡 核心原則:三種超時必須同時配置、分層級設定、嚴格關閉響應、持續監控指標。HttpClient 5.x 建議遷移至
RequestConfig.setResponseTimeout統一語義,並利用Timeout物件避免單位混淆。