Apache HttpClient 4.5.x 學習總結五:HTTP execution context(HTTP執行上下文)
翻譯:
1.3. HTTP 執行上下文
HTTP 設計初衷是無狀態的請求-響應協議,但實際應用常需在多個邏輯關聯的請求間保持狀態信息。為此,HttpClient 允許在特定執行上下文(HTTP Context)中執行請求。通過在連續請求間復用相同上下文,多個邏輯關聯的請求可組成邏輯會話。HTTP 上下文功能類似 java.util.Map<String, Object>,本質是命名值的集合。應用可在請求執行前填充上下文屬性,或在執行后檢查上下文。
注意:HttpContext 可包含任意對象,非線程安全。建議每個執行線程維護獨立上下文。
HttpClient 在請求執行期間自動注入以下上下文屬性:
HttpConnection:表示到目標服務器的實際連接HttpHost:表示連接目標HttpRoute:表示完整連接路由HttpRequest:實際 HTTP 請求(最終發送狀態)HttpResponse:實際 HTTP 響應Boolean:標記請求是否已完整傳輸到目標RequestConfig:實際請求配置List<URI>:請求執行中接收的所有重定向位置
可使用 HttpClientContext 工具類簡化上下文操作:
HttpContext context = <...>;
HttpClientContext clientContext = HttpClientContext.adapt(context); // 上下文適配
HttpHost target = clientContext.getTargetHost(); // 獲取目標主機
HttpRequest request = clientContext.getRequest(); // 獲取實際請求
RequestConfig config = clientContext.getRequestConfig(); // 獲取請求配置
邏輯會話實踐:
屬于同一邏輯會話的請求序列應共享 HttpContext 實例,確保上下文狀態自動傳播:
CloseableHttpClient httpclient = HttpClients.createDefault();
// 初始配置(設置超時)
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(1000)
.setConnectTimeout(1000)
.build();
// 請求1:注入配置
HttpGet httpget1 = new HttpGet("http://localhost/1");
httpget1.setConfig(requestConfig);
// 執行請求并傳遞上下文
CloseableHttpResponse response1 = httpclient.execute(httpget1, context);
try { /* 處理響應1 */ } finally { response1.close(); }
// 請求2:自動繼承相同上下文中的配置!
HttpGet httpget2 = new HttpGet("http://localhost/2");
CloseableHttpResponse response2 = httpclient.execute(httpget2, context);
try { /* 處理響應2 */ } finally { response2.close(); }
深度解讀:
1. 上下文的核心價值
graph LR
A[無狀態協議] --> B[業務需狀態保持]
B --> C[解決方案]
C --> D1(Cookie)
C --> D2(Session)
C --> D3(HttpContext)
D3 --> E[跨請求共享數據]
- 本質:在無狀態協議上構建有狀態會話
- 典型場景:登錄態保持、連續API調用、跨請求配置傳遞
2. 線程安全陷阱與規避
- 危險操作:
// 錯誤!多線程共享同一上下文 public static HttpContext SHARED_CTX = new BasicHttpContext(); - 正確姿勢:
// 每個線程獨立上下文 (如Spring@Scope("request")) ThreadLocal<HttpContext> threadCtx = new ThreadLocal<>();
3. 自動注入的黃金八屬性
| 屬性 | 關鍵信息 | 應用場景 |
|---|---|---|
HttpConnection |
底層TCP連接對象 | 監控連接狀態 |
HttpRoute |
完整路由路徑(含代理) | 分析網絡拓撲 |
List<URI> |
重定向軌跡 | 調試跳轉異常 |
RequestConfig |
超時/代理等配置 | 配置繼承機制的核心 |
4. 配置傳播機制解析
// 初始請求設置配置
httpget1.setConfig(customConfig); // (1) 顯式設置
client.execute(httpget1, context);
// 后續請求自動繼承
client.execute(httpget2, context); // (2) 從context讀取配置
- 實現原理:
首次執行時將RequestConfig存入上下文,后續請求通過HttpClientContext.getRequestConfig()優先使用上下文配置
5. 企業級應用實踐
- 用戶會話保持:
// 登錄后存儲token到上下文 context.setAttribute("AUTH_TOKEN", extractToken(response)); // 后續請求自動攜帶token httpGet.addHeader("Authorization", context.getAttribute("AUTH_TOKEN").toString()); - 全鏈路監控:
// 獲取連接創建時間戳 long connectStart = (Long)context.getAttribute("CONNECT_START"); // 計算真實網絡耗時 long latency = System.currentTimeMillis() - connectStart;
警示:上下文生命周期通常與業務會話綁定,切忌將其存入靜態變量或長時間存活的Bean(易引發內存泄漏)。
浙公網安備 33010602011771號