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号