前言
**HTTP(Hypertext Transfer Protocol)**协议是用于从服务器传输超文本到本地浏览器的传送协议。目前正在被广泛使用的是1999年发布的HTTP/1.0,直到2015年HTTP/2才成为标准。
HTTP/1.1和HTTP/2最主要的区别是客户端和服务器之间传输数据的方式。HTTP/1.1依赖于请求和响应周期。HTTP/2允许服务器向客户端"push"数据,它可以向客户端发送比客户端请求更多的数据。这使得它可以优先处理发送对于首先加载网页至关重要的数据。
从Java9提供了新的API来处理HTTP调用,JDK中新的HTTP客户端(HttpClient)在Java9中还属于孵化器模块,经历了JEP110,到Java11 的JEP321,在Java11版本正式"转正"。
官方JEP
JDK9:JEP 110: HTTP/2 Client (Incubator)
JDK11:JEP 321: HTTP Client
在Java9之前,Java提供的HTTP客户端是HttpURLConnection
,
- 它是基于
URLConnection
API设计的,URLConnection
设计之初本意是可以支持多种协议,例如ftp,gopher等等,但是目前这些协议都已经失效了, HttpURLConnection
的API是早于HTTP/1.1发布前就设计了,它的API太过抽象。HttpURLConnection
也是基于阻塞模式(blocking mode)开发的,要求每个就请求/响应都有一个单独的线程- 继续维护下去变得十分困难。
开发JDK的大叔因此设计了全新的HTTP客户端用于替换HttpURLConnection
。新的Http客户端不仅支持HTTP/1.1,还提供HTTP/2以及WebSocket的支持。在性能方面也有了大幅提升,按照JEP中的描述,新的HTTP客户端有着不输于Apache HttpClient库以及Netty和Jetty的性能。
API实战
Http Client的主要类有下面三个,
java.net.http.HttpClient
java.net.http.HttpRequest
java.net.http.HttpResponse
在jdk9中,HTTP Client客户端API都可以从
jdk.incubator.httpclient
模块中获取,在JDK11中迁移到java.net.http
HttpClient介绍
HttpClient
是一个抽象类,在HttpClient
中仅定义获取配置的抽象方法,构建HttpClient
可以通过下面两个方法
- httpClient()
- newBuilder()
通过源码可以看到HttpClient其实是通过HttpClientBuilderImpl
构建的
// java.net.http
// 抽象类,不能直接new
public abstract class HttpClient {
// protected
protected HttpClient() {}
// 通过newHttpClient直接构建Http Client
public static HttpClient newHttpClient() {
return newBuilder().build();
}
// 通过Builder的方式构建HTTP Client
public static Builder newBuilder() {
return new HttpClientBuilderImpl();
}
}
HttpClientBuilderImpl
属性如下所示,属性配置完成后调用build()
方法构建HttpClient
,可以看到HttpClient
的实现类其实是HttpClientImpl
// jdk.internal.net.http
public class HttpClientBuilderImpl implements HttpClient.Builder {
// CookieHandler对象提供了一种回调机制,用于将HTTP状态管理策略实现连接到HTTP协议处理程序中
CookieHandler cookieHandler;
Duration connectTimeout;
// 跳转策略
HttpClient.Redirect followRedirects;
// 代理Selector
ProxySelector proxy;
Authenticator authenticator;
// HTTP版本枚举,1.1和2
HttpClient.Version version;
// 线程池,异步调用时使用
Executor executor;
// ssl安全参数
SSLContext sslContext;
SSLParameters sslParams;
int priority = -1;
@Override
public HttpClient build() {
return HttpClientImpl.create(this);
}
}
HttpClientImpl
源码简单分析,我们截取一些关键代码
- 构建
HttpClientImpl
构造器里面的逻辑还是很复杂的,在构造犯法中,HttpClientImpl
默认会创建核心线程数为0的缓存鲜橙汁 - 同步发送请求方法底层其实用的是异步请求方法
- 异步发送请求使用构造函数中生成的代理线程池发送Http请求
// jdk.internal.net.http
final class HttpClientImpl extends HttpClient implements Trackable {
// HttpClient构造方法,
private HttpClientImpl(HttpClientBuilderImpl builder,
SingleFacadeFactory facadeFactory) {
// ...
// 默认给Builder中的线程池
Executor ex = builder.executor;
if (ex == null) {
// 构建缓存线程池
ex = Executors.newCachedThreadPool(new DefaultThreadFactory(id));
isDefaultExecutor = true;
} else {
isDefaultExecutor = false;
}
// 代理线程池
delegatingExecutor = new DelegatingExecutor(this::isSelectorThread, ex);
// ...
}
// 同步发送请求
@Override
public <T> HttpResponse<T>
send(HttpRequest req, BodyHandler<T> responseHandler)
throws IOException, InterruptedException
{
CompletableFuture<HttpResponse<T>> cf = null;
try {
cf = sendAsync(req, responseHandler, null, null);
return cf.get();
} catch (InterruptedException ie) {
if (cf != null )
cf.cancel(true);
throw ie;
} catch (ExecutionException e) {
// ...
}
}
// 异步返送请求
private <T> CompletableFuture<HttpResponse<T>>
sendAsync(HttpRequest userRequest,
BodyHandler<T> responseHandler,
PushPromiseHandler<T> pushPromiseHandler,
Executor exchangeExecutor/*异步推送默认给是delegatingExecutor.delegate*/) {
Objects.requireNonNull(userRequest);
Objects.requireNonNull(responseHandler);
AccessControlContext acc = null;
if (System.getSecurityManager() != null)
acc = AccessController.getContext();
// 为了保证HttpRequest的安全,会克隆HttpRequest
HttpRequestImpl requestImpl = new HttpRequestImpl(userRequest, proxySelector);
if (requestImpl.method().equals("CONNECT"))
throw new IllegalArgumentException("Unsupported method CONNECT");
long start = DEBUGELAPSED ? System.nanoTime() : 0;
reference();
try {
if (debugelapsed.on())
debugelapsed.log("ClientImpl (async) send %s", userRequest)
// 默认使用代理线程池
Executor executor = exchangeExecutor == null
? this.delegatingExecutor : exchangeExecutor;
MultiExchange<T> mex = new MultiExchange<>(userRequest,
requestImpl,
this,
responseHandler,
pushPromiseHandler,
acc);
// 异步获取HttpResponse
CompletableFuture<HttpResponse<T>> res =
mex.responseAsync(executor).whenComplete((b,t) -> unreference());
// ...
if (exchangeExecutor != null) {
res = res.whenCompleteAsync((r, t) -> { /* do nothing */}, ASYNC_POOL);
}
return res;
} catch(Throwable t) {
unreference();
debugCompleted("ClientImpl (async)", start, userRequest);
throw t;
}
}
}
HttpRequest介绍
与HttpClient
类似,HttpRequest
也是一个抽象类,它不能通过构造方法直接创建,需要通过newBuilder()
生成HttpRequest.Builder
// java.net.http
public abstract class HttpRequest {
// protected构造方法
protected HttpRequest() {}
// 带URI的newBuilder方法
public static HttpRequest.Builder newBuilder(URI uri) {
return new HttpRequestBuilderImpl(uri);
}
// 不带URI的newBuilder方法
public static HttpRequest.Builder newBuilder() {
return new HttpRequestBuilderImpl();
}
}
HttpRequest.Builder
是一个接口,从源码可以看出它的实现类是HttpRequestBuilderImpl
// jdk.internal.net.http
public class HttpRequestBuilderImpl implements HttpRequest.Builder {
// HttpHeaders建造者
private HttpHeadersBuilder headersBuilder;
// http URI
private URI uri;
// http method名称
private String method;
private boolean expectContinue;
// BodyPublisher将高级Java对象转换为适合作为请求体发送的字节缓冲区流。BodyPublishers类提供了许多常见发布程序的实现。BodyPublisher接口扩展了Flow。Publisher<ByteBuffer>,这意味着BodyPublisher充当字节缓冲区的发布者
private BodyPublisher bodyPublisher;
// http版本
private volatile Optional<HttpClient.Version> version;
// http超时时间
private Duration duration;
// 创建HttpRequest
@Override
public HttpRequest build() {
if (uri == null)
throw new IllegalStateException("uri is null");
assert method != null;
return new ImmutableHttpRequest(this);
}
}
通过HttpRequestBuilderImpl
的build()
方法构建的HttpRequest
的实现类是ImmutableHttpRequest
,ImmutableHttpRequest
中的参数与HttpRequestBuilderImpl
相同
// jdk.internal.net.http
final class ImmutableHttpRequest extends HttpRequest {
private final String method;
private final URI uri;
private final HttpHeaders headers;
private final Optional<BodyPublisher> requestPublisher;
private final boolean expectContinue;
private final Optional<Duration> timeout;
private final Optional<Version> version;
ImmutableHttpRequest(HttpRequestBuilderImpl builder) {
this.method = Objects.requireNonNull(builder.method());
this.uri = Objects.requireNonNull(builder.uri());
this.headers = HttpHeaders.of(builder.headersBuilder().map(), ALLOWED_HEADERS);
this.requestPublisher = Optional.ofNullable(builder.bodyPublisher());
this.expectContinue = builder.expectContinue();
this.timeout = Optional.ofNullable(builder.timeout());
this.version = Objects.requireNonNull(builder.version());
}
}
HttpResponse介绍
HttpResponse
是HTTP请求结果的接口,我们不需要手动去创建它,HttpResponse
在HttpClient
发送请求,HttpResponse
在接收到响应状态代码和标头时可用,通常在完全接收到响应主体之后可用。在完全接收到响应主体之前,HttpResponse
是否可用取决于发送HttpRequest
时提供的BodyHandler
public interface HttpResponse<T> {
// 获取HTTP statusCode
public int statusCode();
// 获取HTTP Response关联的request
public HttpRequest request();
// 返回前一个response
public Optional<HttpResponse<T>> previousResponse();
// 返回前一个HttpHeaders
public HttpHeaders headers();
// 返回HTTP response body
public T body();
// 获取sslSession
public Optional<SSLSession> sslSession();
// 获取HTTP URI
public URI uri();
// 获取HTTP 版本
public HttpClient.Version version();
// 初始化返回信息给BodyHandler,它创建的时机是已经获取到了HTTP statusCode和HTTP headers,处理Http response body前
public interface ResponseInfo {
// 获取http statusCode
public int statusCode();
// 获取http headers
public HttpHeaders headers();
// 获取http版本
public HttpClient.Version version();
}
@FunctionalInterface
public interface BodyHandler<T> {
// ,返回一个CompleteStage
public BodySubscriber<T> apply(ResponseInfo responseInfo);
}
// BodyHandler的静态类,提供了不同的BodyHandler实现的实现方法
public static class BodyHandlers {
private BodyHandlers() { }
// ...
// 将
public static BodyHandler<String> ofString(Charset charset) {
Objects.requireNonNull(charset);
return (responseInfo) -> BodySubscribers.ofString(charset);
}
// ...
}
// push承诺请求handler,用于HTTP/2
public interface PushPromiseHandler<T> {
public void applyPushPromise(
HttpRequest initiatingRequest,
HttpRequest pushPromiseRequest,
Function<HttpResponse.BodyHandler<T>,CompletableFuture<HttpResponse<T>>> acceptor
);
public static <T> PushPromiseHandler<T>
of(Function<HttpRequest,BodyHandler<T>> pushPromiseHandler,
ConcurrentMap<HttpRequest,CompletableFuture<HttpResponse<T>>> pushPromisesMap) {
return new PushPromisesHandlerWithMap<>(pushPromiseHandler, pushPromisesMap);
}
}
// BodySubscriber用于消费response body bytes并将其转成更高级的java类型,例如String等
public interface BodySubscriber<T>
extends Flow.Subscriber<List<ByteBuffer>> {
public CompletionStage<T> getBody();
}
// 生成BodySubscriber的静态工具方法,它提供了许多BodySubscriber的通用实现
public static class BodySubscribers {
private BodySubscribers() { }
// ...
public static BodySubscriber<String> ofString(Charset charset) {
Objects.requireNonNull(charset);
return new ResponseSubscribers.ByteArraySubscriber<>(
bytes -> new String(bytes, charset)
);
}
// ...
}
}
HTTP Client实战
使用新的Http Client发送Http请求只需要遵循下面4个步骤
- 通过
HttpClient.newHttpClient()
创建HttpClient
- 通过
HttpRequest.newBuilder()
创建HttpRequest
- 调用
HttpClient
发送请求方法发送Http请求 - 处理返回结果
同步例子
同步Http请求使用send()
方法
// 1.创建HttpClient
HttpClient client = HttpClient.newHttpClient();
// 2.创建HttpRequest
HttpRequest request = HttpRequest.newBuilder()
.GET()
.uri(new URI("http://localhost:8080/linshifu/name?id=1"))
.build();
System.out.println(LocalDateTime.now()+"===> 开始发送发送请求");
// 3.发送http请求
HttpResponse<String> response = client.send(request, (responseInfo ->
HttpResponse.BodySubscribers.ofString(StandardCharsets.UTF_8)
));
// 4.处理返回结果
System.out.println(LocalDateTime.now()+"===> 结束发送请求,请求结果:\n"
+response.body());
异步例子
异步发送Http请求使用的是sendAsync()
方法,返回CompletableFuture<ResponseBody<T>>
// 1.创建HttpClient
HttpClient client = HttpClient.newHttpClient();
// 2.创建HttpRequest
HttpRequest request = HttpRequest.newBuilder()
.GET()
.uri(new URI("http://localhost:8080/linshifu/name?id=1"))
.build();
System.out.println(LocalDateTime.now()+"===> 开始发送发送请求");
// 3.sendAsync异步请求,返回CompleteFuture
CompletableFuture<HttpResponse<String>> asyncResult = client.sendAsync(request, (responseInfo ->
HttpResponse.BodySubscribers.ofString(StandardCharsets.UTF_8)
));
// 4.处理CompletableFuture结果
asyncResult.thenAccept(response -> {
System.out.println(LocalDateTime.now() + "===> 结束发送请求,请求结果:\n"
+ response.body());
});
asyncResult.get();