您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關(guān)如何使用Java 11 HTTP Client API實現(xiàn)HTTP/2服務(wù)器推送,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
對 HttpUrlConnection 你還有印象嗎?JDK 11為 HttpUrlConnection 重新設(shè)計了 HTTP Client API。HTTP Client API 使用簡單,支持 HTTP/2(默認)和 HTTP/1.1。為了向后兼容,當服務(wù)器不支持 HTTP/2時,HTTP Client API 會自動從 HTTP/2 降到 HTTP1.1。
此外,HTTP Client API 支持同步和異步編程模型,并依靠 stream 傳輸數(shù)據(jù)(reactive stream)。它還支持 WebSocket 協(xié)議,用于實時 Web 應用程序,降低客戶端與服務(wù)器間通信開銷。
除了多路復用(Multiplexing),HTTP/2 另一個強大的功能是服務(wù)器推送。傳統(tǒng)方法(HTTP/1.1)中,主要通過瀏覽器發(fā)起請求 HTML 頁面,解析接收的標記(Markup)并標識引用的資源(例如JS、CSS、圖像等)。
為了獲取資源,瀏覽器會繼續(xù)發(fā)送資源請求(每個資源一個請求)。相反,HTTP/2 會發(fā)送 HTML 頁面和引用的資源,不需要瀏覽器主動請求。因此,瀏覽器請求 HTML 頁面后,就能收到頁面以及顯示所需的所有其他信息。HTTP Client API 通過 PushPromiseHandler 接口支持 HTTP/2 功能。
接口實現(xiàn)必須作為 send() 或 sendAsync() 方法的第三個參數(shù)填入。PushPromiseHandler 依賴下面三項協(xié)同:
客戶端發(fā)起的 send request(initiatingRequest)
合成 push request(pushPromiseRequest)
acceptor 函數(shù),必須成功調(diào)用該函數(shù)才能接受 push promise(acceptor)
調(diào)用特定 acceptor 函數(shù)接受 push promise。acceptor 函數(shù)必須傳入一個 BodyHandler(不能為 null)用來處理 Promise 的 request body。acceptor 函數(shù)會返回一個 CompletableFuture 實例,完成 promise response。
基于以上信息,看一下 PushPromiseHandler 實現(xiàn):
private static final List<CompletableFuture<Void>>
asyncPushRequests =
new CopyOnWriteArrayList<>();
...
private static HttpResponse.PushPromiseHandler<String> pushPromiseHandler() {
return (HttpRequest initiatingRequest,
HttpRequest pushPromiseRequest,
Function<HttpResponse.BodyHandler<String> ,
CompletableFuture<HttpResponse<String>>> acceptor) -> {
CompletableFuture<Void> pushcf =
acceptor.apply(HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept((b) -> System.out.println(
"\nPushed resource body:\n " + b));
asyncPushRequests.add(pushcf);
System.out.println("\nJust got promise push number: " +
asyncPushRequests.size());
System.out.println("\nInitial push request: " +
initiatingRequest.uri());
System.out.println("Initial push headers: " +
initiatingRequest.headers());
System.out.println("Promise push request: " +
pushPromiseRequest.uri());
System.out.println("Promise push headers: " +
pushPromiseRequest.headers());
};
}
現(xiàn)在,觸發(fā)一個 request 把 PushPromiseHandler 傳給 sendAsync():
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://http2.golang.org/serverpush"))
.build();
client.sendAsync(request,
HttpResponse.BodyHandlers.ofString(), pushPromiseHandler())
.thenApply(HttpResponse::body)
.thenAccept((b) -> System.out.println("\nMain resource:\n" + b))
.join();
asyncPushRequests.forEach(CompletableFuture::join);
System.out.println("\nFetched a total of " +
asyncPushRequests.size() +
" push requests");
完整源代碼可在 GitHub 上找到。
github.com/PacktPublishing/Java-Coding-Problems/tree/master/Chapter13/P268_ServerPush
如果要把所有 push promise 及 response 匯總到指定的 map 中,可以使用 PushPromiseHandler.of() 方法,如下所示:
privatestatic final ConcurrentMap<HttpRequest,
CompletableFuture<HttpResponse<String>>> promisesMap
= new ConcurrentHashMap<>();
privatestatic final Function<HttpRequest,
HttpResponse.BodyHandler<String>> promiseHandler
= (HttpRequest req) -> HttpResponse.BodyHandlers.ofString();
public static void main(String[] args)
throws IOException, InterruptedException {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://http2.golang.org/serverpush"))
.build();
client.sendAsync(request,
HttpResponse.BodyHandlers.ofString(), pushPromiseHandler())
.thenApply(HttpResponse::body)
.thenAccept((b) -> System.out.println("\nMain resource:\n" + b))
.join();function(){ //外匯跟單www.gendan5.com System.out.println("\nPush promises map size: " +
promisesMap.size() + "\n");
promisesMap.entrySet().forEach((entry) -> {
System.out.println("Request = " + entry.getKey() +
", \nResponse = " + entry.getValue().join().body());
});
}
privatestatic HttpResponse.PushPromiseHandler<String> pushPromiseHandler() {
return HttpResponse.PushPromiseHandler.of(promiseHandler, promisesMap);
}
完整源代碼可在 GitHub 上找到。
github.com/PacktPublishing/Java-Coding-Problems/tree/master/Chapter13/P268_ServerPushToMap
前面兩個解決方案中 BodyHandler 都用到了 String 類型的 ofString()。如果服務(wù)器還需要推送二進制數(shù)據(jù)(比如圖像),就不是很適用。因此,如果要處理二進制數(shù)據(jù),則需要用 ofByteArray() 切換到byte[] 類型的 BodyHandler。也可以用 ofFile() 把 push 資源保存到磁盤,下面的解決方案是之前方案的改進版:
private static final ConcurrentMap<HttpRequest,
CompletableFuture<HttpResponse<Path>>>
promisesMap =
new ConcurrentHashMap<>();
private static final Function<HttpRequest,
HttpResponse.BodyHandler<Path>> promiseHandler
= (HttpRequest req) -> HttpResponse.BodyHandlers.ofFile(
Paths.get(req.uri().getPath()).getFileName());
public static void main(String[] args)
throws IOException, InterruptedException {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://http2.golang.org/serverpush"))
.build();
client.sendAsync(request, HttpResponse.BodyHandlers.ofFile(
Path.of("index.html")), pushPromiseHandler())
.thenApply(HttpResponse::body)
.thenAccept((b) -> System.out.println("\nMain resource:\n" + b))
.join();
System.out.println("\nPush promises map size: " +
promisesMap.size() +
"\n");
promisesMap.entrySet().forEach((entry) -> {
System.out.println("Request = " + entry.getKey() +
", \nResponse = " + entry.getValue().join().body());
});
}
private static HttpResponse.PushPromiseHandler<Path> pushPromiseHandler() {
return HttpResponse.PushPromiseHandler.of(promiseHandler, promisesMap);
}
關(guān)于“如何使用Java 11 HTTP Client API實現(xiàn)HTTP/2服務(wù)器推送”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。