Java HttpClient
JDK HttpClient 发送 HTTP 请求:同步/异步、超时与响应体处理基础。
#concept
#growing
#dev
#java
[!info] related notes
Java HttpClient
这个概念为什么出现
服务常调用下游 HTTP API。JDK 11+ java.net.http.HttpClient 提供现代客户端,减少对第三方的基础依赖。
[!abstract] 一句话理解
HttpClient+HttpRequest+HttpResponse发送 HTTP;应设置超时、处理非 2xx,并避免在错误线程模型上阻塞。
最小可运行示例
场景:调用本机健康检查(JDK 21 示例,需服务已启动)
import java.net.URI;
import java.net.http.*;
import java.time.Duration;
public class HealthClient {
public static void main(String[] args) throws Exception {
URI endpoint = URI.create(args.length == 0 ? "http://localhost:8080/health" : args[0]);
HttpRequest req = HttpRequest.newBuilder(endpoint)
.timeout(Duration.ofSeconds(3))
.GET()
.build();
try (HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(2))
.build()) {
HttpResponse<Void> resp = client.send(req, HttpResponse.BodyHandlers.discarding());
if (resp.statusCode() < 200 || resp.statusCode() >= 300) {
throw new IllegalStateException("health status=" + resp.statusCode());
}
System.out.println(resp.statusCode());
}
}
}
javac HealthClient.java && java HealthClient
先启动 HTTP 服务示例,再运行客户端,成功时输出 200;可通过第一个命令行参数指定其他健康检查 URL。连接失败、超时、中断或非 2xx 均使本例以异常退出,不把失败伪装成成功。
HttpClient 自 Java 11 提供;本例使用 Java 21 新增的 AutoCloseable 生命周期支持。长期服务通常复用客户端连接池,在服务停机时关闭,而不是每次请求新建。
结合场景再看三个关注点
- 超时必设。
- HTTP 4xx/5xx 本身不会让 send 自动抛异常,BodyHandler 也不会自动将其视为失败;需按契约检查状态。
- 异步 sendAsync 返回 CF。
核心概念与准确模型
1. 请求与构建器
构建出的 HttpRequest 不可变;Builder 可变,不能把 builder 当作线程安全的不可变请求共享。
2. BodyHandlers/BodyPublishers
BodyPublisher 提供请求体,BodyHandler 决定响应体如何消费。ofString/ofByteArray 聚合完整响应体;大响应可用流式处理,但 ofInputStream 返回的流必须读完或关闭。discarding 忽略内容但仍消费响应体。
3. 重定向与 HTTP 版本
默认重定向策略是 NEVER;客户端默认偏好 HTTP/2,协商结果仍可能是 HTTP/1.1,应看 response.version()。
边界情况与反直觉行为
- 代理与 TLS 信任库。
- 大下载要流式 handler。
- send 可抛 InterruptedException;若捕获后不再向上传递,应恢复中断标志。取消 sendAsync 返回的 Future 是尽力中止操作,不保证服务端尚未执行请求。
常见误区
[!warning] 常见误区:忽略状态码只读 body 错误页 HTML 会被当成功解析。
工程实践
- 统一客户端配置(超时、指标)。
- 重试需结合故障类型、幂等性、请求体能否重放与剩余时间预算;超时不证明服务端没有完成操作。
- 传播 correlation id 头。
- 契约测试下游。
本节总结
- JDK 现代 HTTP 客户端
- 超时+状态码
- 同步/异步可选
自测题
- connectTimeout 与 request timeout 差别直觉?
- 何时用 sendAsync?
参考答案
- connectTimeout 只约束新连接建立,复用连接时不生效;请求 timeout 约束等待响应。流式 handler 可能在响应体读完前返回,不能将其当作后续流读取的逐次空闲超时,完整消费仍需应用级期限。
- 需要非阻塞组合多个下游调用时。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| HttpClient API | API | 客户端 |