Apache HttpClient 是 Java 呼叫 HTTP API 的經典選擇。本文提供生產級 RequestUtil 封裝,涵蓋 JSON POST 請求建構、連線池管理、超時配置、回應處理與例外轉換,並說明 HttpClient 4.5+ 至 5.x 遷移要點與現代化替代方案。
package com.example.http;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class RequestUtil {
// 共用客戶端(線程安全、連線池預設 200 路徑、20 路徑/路由)
private static final CloseableHttpClient HTTP_CLIENT = HttpClients.custom()
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectTimeout(5000) // 連線超時 5s
.setSocketTimeout(15000) // 讀取超時 15s
.setConnectionRequestTimeout(1000) // 從池取連線超時 1s
.build())
.evictIdleConnections(30, TimeUnit.SECONDS) // 定期清理閒置連線
.build();
/**
* 發送 JSON POST 請求
* @param url 目標 URL
* @param json JSON 字串(可為 null/空,發送空物件 {})
* @return 回應本文字串
* @throws IOException 網路/IO 異常
*/
public static String postJson(String url, String json) throws IOException {
HttpPost post = buildJsonPost(url, json);
try (CloseableHttpResponse response = HTTP_CLIENT.execute(post)) {
return parseResponse(response);
}
}
private static HttpPost buildJsonPost(String url, String json) {
HttpPost post = new HttpPost(url);
post.setHeader("Accept", "application/json");
post.setHeader("Content-Type", "application/json; charset=UTF-8");
String payload = StringUtils.isBlank(json) ? "{}" : json;
post.setEntity(new StringEntity(payload, StandardCharsets.UTF_8));
return post;
}
private static String parseResponse(CloseableHttpResponse response) throws IOException {
HttpEntity entity = response.getEntity();
if (entity == null) return "{}";
try {
return EntityUtils.toString(entity, StandardCharsets.UTF_8);
} finally {
EntityUtils.consumeQuietly(entity); // 確保釋放連線回池
}
}
// 應用關閉時呼叫釋放資源
public static void shutdown() throws IOException {
HTTP_CLIENT.close();
}
}
CloseableHttpClient 線程安全、內建連線池,**全應用僅建立一次**,避免頻繁建立/關閉導致連線耗盡與 TIME_WAIT 堆積。ConnectTimeout:TCP 三向交握超時。SocketTimeout:資料讀取無回應超時(業務邏輯處理時間)。ConnectionRequestTimeout:從連線池取得可用連線超時(高併發防護)。EntityUtils.consumeQuietly(entity) 或 try-with-resources 確保連線歸還池中,否則連線洩漏導致後續請求卡死。StandardCharsets.UTF_8 避免平台預設編碼差異導致中文亂碼。
// 1. 重試機制(針對冪等請求)
HttpClientBuilder.create()
.setRetryHandler(new DefaultHttpRequestRetryHandler(3, true)) // 重試 3 次
.build();
// 2. 熔斷整合(Resilience4j + HttpClient)
// 需自訂 HttpRequestInterceptor 搭配 CircuitBreaker
// 3. 監控指標(Micrometer)
// 攔截器記錄:請求耗時、狀態碼分布、錯誤率、連線池使用率
| 4.x 類別 | 5.x 對應 | 備註 |
|---|---|---|
| CloseableHttpClient | CloseableHttpClient | 套件變更:org.apache.http.client → org.apache.hc.client5.http |
| HttpPost | HttpPost | 建構方式相同 |
| StringEntity | StringEntity | 套件變更 |
| EntityUtils | EntityUtils | 方法簽名微調 |
| RequestConfig | RequestConfig | Builder 模式統一 |
| HttpClients.custom() | HttpClients.custom() | 配置 API 大同小異 |
Maven 依賴變更:
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.3.1</version>
</dependency>
java.net.http.HttpClient,支援 HTTP/2、WebSocket、CompletableFuture 非同步、零依賴。| 錯誤現象 | 可能原因 | 排查方向 |
|---|---|---|
| Connection refused | 目標服務未啟動/防火牆/端口錯誤 | telnet/curl 驗證網路連通性 |
| Read timed out | SocketTimeout 過短/下游處理慢 | 調整 SocketTimeout、檢查下游效能 |
| Pool exhausted | 連線洩漏/池大小不足/併發過高 | 檢查 EntityUtils.consume、調大 MaxTotal/MaxPerRoute |
| SSLHandshakeException | 憑證不信任/主機名驗證失敗 | 信任憑證鏈、或設定信任所有(僅測試) |