新聞中心
古老的背景
HttpURLConnection并不是不能使用,由于不需要依賴,在一些demo項(xiàng)目的時(shí)候也會偶爾拿來用。但HttpURLConnection本身已經(jīng)太過古早,并且很難說HttpURLConnection能夠勝任包含各種鑒權(quán)信息、各種COOKIE信息的訪問請求。

針對這種情況網(wǎng)絡(luò)上各種大神提供了更多高級的封裝,比較流行的有Apache的HttpClient、Okhttp Client、Spring Cloud Feign之類的。這些封裝提供了更豐富的資源與更便捷的封裝,也支持了更高級功能如HTTP/2協(xié)議、異步請求等。
不過到了JDK9的時(shí)候,Java提供了一個(gè)新的Http請求工具HttpClient,經(jīng)過了JDK10的再次預(yù)覽,最終在JAVA11中作為正式功能提供使用,同時(shí)也完全替換了僅有阻塞模式的HttpURLConnection。
HttpClient簡介
作為JDK11中正式推出的新Http連接器,支持的功能還是比較新的,主要的特性有:
- 完整支持HTTP 2.0 或者HTTP 1.1
- 支持 HTTPS/TLS
- 有簡單的阻塞使用方法
- 支持異步發(fā)送,異步時(shí)間通知
- 支持WebSocket
- 支持響應(yīng)式流
HTTP2.0其他的客戶端也能支持,而HttpClient使用CompletableFuture作為異步的返回?cái)?shù)據(jù)。WebSocket的支持則是HttpClient的優(yōu)勢。響應(yīng)式流的支持是HttpClient的一大優(yōu)勢。
而HttpClient中的NIO模型、函數(shù)式編程、CompletableFuture異步回調(diào)、響應(yīng)式流讓HttpClient擁有極強(qiáng)的并發(fā)處理能力,所以其性能極高,而內(nèi)存占用則更少。
HttpClient的主要類有:
- java.net.http.HttpClient
- java.net.http.HttpRequest
- java.net.http.HttpResponse
- java.net.http.WebSocket(本文就不介紹這個(gè)了)
細(xì)節(jié)會在后文介紹,但是WebSocket用的比較少,本文就略過了。
核心使用
HttpClient 的核心類主要就是 HttpClient、HttpRequest以及HttpResponse,它們都是位于java.net.http 包下,接下來對他們進(jìn)行一下介紹。
HttpClient
HttpClient類是最核心的類,它支持使用建造者模式進(jìn)行復(fù)雜對象的構(gòu)建,主要的參數(shù)有:
- Http 協(xié)議的版本 (HTTP 1.1 或者 HTTP 2.0),默認(rèn)是 2.0。
- 是否遵從服務(wù)器發(fā)出的重定向
- 連接超時(shí)時(shí)間
- 代理
- 認(rèn)證
//可以用參數(shù)調(diào)整
HttpClient client = HttpClient.newBuilder()
.version(Version.HTTP_1_1)
.followRedirects(Redirect.NORMAL)
.connectTimeout(Duration.ofSeconds(20))
.proxy(ProxySelector.of(new InetSocketAddress("proxy.cdxwcx.com", 8080)))
.authenticator(Authenticator.getDefault())
.build();
//也可以直接全部默認(rèn)的便捷創(chuàng)建
HttpClient clientSimple = HttpClient.newHttpClient();
當(dāng)創(chuàng)建了HttpClient實(shí)例后,可以通過其發(fā)送多條請求,不用重復(fù)創(chuàng)建。
HttpRequest
HttpRequest 是用語描述請求體的類,也支持通過建造者模式構(gòu)建復(fù)雜對象,主要的參數(shù)有:
- 請求地址
- 請求方法:GET,POST,DELETE 等(默認(rèn)是GET)
- 請求體 (按需設(shè)置,例如 GET 不用 body,但是 POST 要設(shè)置)
- 請求超時(shí)時(shí)間(默認(rèn))
- 請求頭
//使用參數(shù)組合進(jìn)行對象構(gòu)建,讀取文件作為請求體
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://www.baidu.com"))
.timeout(Duration.ofSeconds(20))
.header("Content-type","application/json")
.POST(HttpRequest.BodyPublishers.ofFile(Paths.get("data.json")))
.build();
//直接GET訪問
HttpRequest requestSimple = HttpRequest.newBuilder(URI.create("http://www.baidu.com")).build();
HttpRequest 是一個(gè)不可變類,可以被多次發(fā)送。
HttpResponse
HttpResponse沒有提供外部可以創(chuàng)建的實(shí)現(xiàn)類,它是一個(gè)接口,從client的返回值中創(chuàng)建獲得。接口中的主要方法為:
public interface HttpResponse{
public int statusCode();
public HttpRequest request();
public Optional> previousResponse();
public HttpHeaders headers();
public T body();
public URI uri();
public OptionalsslSession();
public HttpClient.Version version();
}
HttpResponse的返回內(nèi)容與常識一致,這里就不展開介紹了。
信息發(fā)送
HttpClient中可以使用同步發(fā)送或者異步發(fā)送。
同步 send()
同步發(fā)送后,請求會一直阻塞到收到response為止。
final HttpResponsesend = client.send(httpRequest, HttpResponse.BodyHandlers.ofString());
System.out.println(send.body());
其中 send的第二個(gè)參數(shù)是通過HttpResponse.BodyHandlers的靜態(tài)工廠來返回一個(gè)可以將 response 轉(zhuǎn)換為目標(biāo)類型T的處理器(handler),本例子中的類型是String。
HttpResponse.BodyHandlers.ofString()的實(shí)現(xiàn)方法為:
public static BodyHandlerofString() {
return (responseInfo) -> BodySubscribers.ofString(charsetFrom(responseInfo.headers()));
}
其中,BodySubscribers.ofString() 的方法實(shí)現(xiàn)是:
public static BodySubscriberofString(Charset charset) {
Objects.requireNonNull(charset);
return new ResponseSubscribers.ByteArraySubscriber<>(
bytes -> new String(bytes, charset)
);
}
可以看到最終是返回了一個(gè)ResponseSubscribers ,而Subscribers則是我們之前《JDK9響應(yīng)式編程》中討論過的訂閱者。這個(gè)構(gòu)造方法的入?yún)unction定義了訂閱者中的finisher屬性,而這個(gè)屬性將在響應(yīng)式流完成訂閱的時(shí)在onComplete()`方法中調(diào)用。
異步 sendAsync()
異步請求發(fā)送之后,會立刻返回 CompletableFuture,然后可以使用CompletableFuture中的方法來設(shè)置異步處理器。
client.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println)
.join();
而就如同JDK中響應(yīng)式流中發(fā)布者的submit()方法與offer()方法一樣,HttpClient中的send()方法知識sendAsync方法的特例,在send()方法中是先調(diào)用sendAsync()方法,然后直接阻塞等待響應(yīng)結(jié)束再返回,部分核心代碼為:
@Override
publicHttpResponse
send(HttpRequest req, BodyHandlerresponseHandler)
throws IOException, InterruptedException
{
CompletableFuture> cf = null;
// if the thread is already interrupted no need to go further.
// cf.get() would throw anyway.
if (Thread.interrupted()) throw new InterruptedException();
try {
cf = sendAsync(req, responseHandler, null, null);
return cf.get();
} catch (InterruptedException ie) {
if (cf != null )
cf.cancel(true);
throw ie;
}
...
響應(yīng)式流
HttpClient 作為 Request 的發(fā)布者 (publisher),將 Request 發(fā)布到服務(wù)器,作為 Response 的訂閱者 (subscriber),從服務(wù)器接收 Response。而上文中我們在send()的部分發(fā)現(xiàn),調(diào)用鏈的最底端返回的是一個(gè)ResponseSubscribers訂閱者。
當(dāng)然,就如同
HttpResponse.BodyHandlers.ofString(),HttpClient默認(rèn)提供了一系列的默認(rèn)訂閱者,用語處理數(shù)據(jù)的轉(zhuǎn)換:
HttpRequest.BodyPublishers::ofByteArray(byte[])
HttpRequest.BodyPublishers::ofByteArrays(Iterable)
HttpRequest.BodyPublishers::ofFile(Path)
HttpRequest.BodyPublishers::ofString(String)
HttpRequest.BodyPublishers::ofInputStream(Supplier)
HttpResponse.BodyHandlers::ofByteArray()
HttpResponse.BodyHandlers::ofString()
HttpResponse.BodyHandlers::ofFile(Path)
HttpResponse.BodyHandlers::discarding()
所以在HttpClient的時(shí)候我們也可以自己創(chuàng)建一個(gè)實(shí)現(xiàn)了Flow.Subscriber>接口的訂閱者,用于消費(fèi)數(shù)據(jù)。響應(yīng)式流完整的簡單的例子如下:
public class HttpClientTest {
public static void main(String[] args) throws IOException, InterruptedException {
final HttpClient client = HttpClient.newHttpClient();
final HttpRequest httpRequest = HttpRequest.newBuilder(URI.create("http://www.baidu.com")).build();
HttpResponse.BodySubscriber subscriber = HttpResponse.BodySubscribers.fromSubscriber(new StringSubscriber(),StringSubscriber::getBody);
client.sendAsync(httpRequest,responseInfo -> subscriber)
.thenApply(HttpResponse::body)
.thenAccept(System.out::println)
.join();
}
static class StringSubscriber implements Flow.Subscriber>{
Flow.Subscription subscription;
List response = new ArrayList<>();
String body;
public String getBody(){
return body;
}
@Override
public void onSubscribe(Flow.Subscription subscription) {
this.subscription = subscription;
subscription.request(1);
}
@Override
public void onNext(List item) {
response.addAll(item);
subscription.request(1);
}
@Override
public void onError(Throwable throwable) {
System.err.println(throwable);
}
@Override
public void onComplete() {
byte[] data = new byte[response.stream().mapToInt(ByteBuffer::remaining).sum()];
int offset = 0;
for(ByteBuffer buffer:response){
int remain = buffer.remaining();
buffer.get(data,offset,remain);
offset += remain;
}
body = new String(data);
}
}
}
最后
HttpClient是JDK11正式上線的高性能Http客戶端。其底層基于響應(yīng)式流,通過上層封裝還提供了異步信息發(fā)送、同步信息發(fā)送,以及其他完成的HTTP協(xié)議內(nèi)容。在進(jìn)行響應(yīng)式編程的方面,HttpClient也是一個(gè)十分優(yōu)秀的參照目標(biāo)。
網(wǎng)站名稱:Java11新特性-效能翻倍的HttpClient
鏈接分享:http://www.dlmjj.cn/article/dhdipeh.html


咨詢
建站咨詢
