Java 9預期將於本月27日發佈, 本文由László Csontos發佈在Springuni, 討論了Java 9與HTTP/2有關的特性。
在HTTP/1.1發佈後16年, 國際互聯網工程任務組(IETF)的流媒體工作組在2015年批准了HTTP/2協議。 HTTP/2承諾降低網路延遲, 並棄用很多在HTTP/1.1中所必需的工作流程, 以滿足當今的回應時間要求。 本文將簡要介紹HTTP/2, 並談談它是怎樣使基於文本的HTTP/1.1變得煥然一新的, 以及即將來到的Java 9中對HTTP/2的支援功能。
HTTP/1.1早已變得老舊不堪, 而1999年以來的互聯網已經發生了很多變化。
在互聯網上, 人們正變得越來越沒有耐心, 但他們不會注意到, 如果回應時間低於100毫秒, 那麼他們在網路上表現出的行為並不直接由他們自己實施。
當響應時間達到1秒時, 就會引起人們的注意了, 而當一個網站的響應時間超過10秒時, 那麼它就會被認為發生了故障。 根據一些調查的結果, 人們注意力的平均持續時間已經下降到7-8秒, 所以即使是1秒的延遲也會造成高達7%的收入損失。
針對HTTP/1.1的網路延遲優化技術
對於HTTP/1.1來說, 需要做一些(有時候是很繁重的)工作來滿足當今的要求。
一項類似的優化技術是將多個資源(CSS、JavaScript)捆綁在一起, 這樣就能夠通過一次單獨的請求獲取到這些資源。 這樣做好壞參半, 在節省了一次網路往返過程的同時, 可能會使捆綁資源中的一部分無法使用。 在某些情況下, 複雜的伺服器端邏輯負責選擇適當的靜態資源,
Image sprites圖像精靈是一項與CSS和JavaScript檔捆綁類似的技術, 可以減少請求的數量。
還有另一項技術被用於將靜態資源內嵌到HTML中。
HTTP/2概述
HTTP/2意在減輕為維護HTTP/1.1複雜的底層架構而帶來的痛苦, 以提高HTTP/1.1的性能。 儘管HTTP/2仍然對HTTP/1.1向下相容, 但它已不再是一個基於文本的協定。 當用戶端通過HTTP/1.1請求建立一個連接時, 所有請求將會被升級。 從這一點上看, HTTP/2是用“二進位資料幀”來說話的。
HTTP/2多工
HTTP/2頭部壓縮
HTTP 1.x協定族都是基於文本的, 因此它們都相當冗長。 有時候同一個HTTP頭的集合被一遍又一遍地進行交換。 HTTP/2在整個請求過程中保持HTTP頭 表不變, 因而大大降低了所需的頻寬。 重要的是, 這只是在去耦合, 而不是經典意義上的壓縮。
HTTP/2推送
你可能會認為, HTTP/2推送是某種WebSocket的延續或升級, 但實際上並不是這樣。 WebSocket是用戶端和伺服器之間全雙工通信的一種方法, 一旦TCP連接被建立起來, 伺服器就可以向用戶端發送資料, 而HTTP/2則解決的是與此不同的問題。
HTTP/2推送一種主動向用戶端發送資源的技術, 不必由用戶端發出請求。 這實際上意味著, 伺服器端知道, 一個網站需要一些圖片, 伺服器會在用戶端發出請求前的很長時間內, 就一次性將這些圖片發送到用戶端。
Java HTTP用戶端支援HTTP/2
根據維琪百科關於HTTP/2的一個頁面的說法, 在編寫的時候, 以下Java用戶端庫已能夠建立HTTP/2連接。
Jetty
Netty
OkHttp
x
Firefly
但在這篇文章中, 我們關注的是Java 9提供的HTTP/2支持。 JEP 110 指定了具體要求, 同時聲明該專案仍處於孵化狀態, 這實際上意味著,
JEP 110為新的、內置的HTTP/2用戶端提出了具體要求, 因此它提供了一個高級別的、簡便易用的API和與現有選項相似(或更高)的性能。
第一步是導入模組jdk.incubator.httpclient。
1、module com.springui.echo.client {
2、requires jdk.incubator.httpclient;
3、}
對於這個例子來說, 我們要使用Undertow做為相容HTTP/2的web伺服器。 它用來回應用戶端發送的消息。
public class EchoServer {
private static final Logger LOGGER = Logger.getLogger(EchoServer.class.getSimpleName());
private static final int PORT = 8888;
private static final String HOST = “localhost”;
public static void main(final String[] args) {
Undertow server = Undertow.builder()
.setServerOption(UndertowOptions.ENABLE_HTTP2, true)
.addHttpListener(PORT, HOST)
.setHandler(exchange -> {
LOGGER.info(“Client address is: ” + exchange.getConnection().getPeerAddress().toString());
exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, “text/plain”);
exchange.getRequestReceiver().receiveFullString((e, m) -> e.getResponseSender().send(m));
}).build();
server.start();
}
}
新的API處處遵循生成器模式, 而作為初始化HTTP請求入口的HttpClient也不例外。
HttpClient client = HttpClient
.newBuilder()
.version(Version.HTTP_2)
.build();
一旦我們有了一個HttpClient實例, 就可以通過一個生成器建立更多的HttpClient實例。
以阻塞模式發送請求
HttpResponse response = client.send(
HttpRequest
.newBuilder(TEST_URI)
.POST(BodyProcessor.fromString(“Hello world”))
.build(),
BodyHandler.asString()
);
以非阻塞模式發送請求
請求被處理多久, send方法就會阻塞多久, 但還是有一種方法來非同步交換HTTP消息:以非阻塞模式發送請求。
在下面的例子中,
List .ints(10) .mapToObj(String::valueOf) .map(message -> client .sendAsync( HttpRequest.newBuilder(TEST_URI) .POST(BodyProcessor.fromString(message)) .build(), BodyHandler.asString() ) .thenApply(r -> r.body()) ) .collect(Collectors.toList()); CompletableFuture.allOf(responseFutures.toArray(new CompletableFuture[0])).join(); responseFutures.stream().forEach(future -> { LOGGER.info(“Async response: ” + future.getNow(null)); }); 處理push-promise架構 以上全部例子都可以是過時的HTTP/1.1請求。
除了創建HttpClient以外,
沒有看到任何HTTP/2所特有的東西。
這個用戶端API中與HTTP/2最有關聯的功能很可能是當HTTP/2推送被使用時它處理多個回應的方式。
Map client.sendAsync( HttpRequest.newBuilder(TEST_URI) .POST(BodyProcessor.fromString(TEST_MESSAGE)) .build(), MultiProcessor.asMap(request -> Optional.of(BodyHandler.asString())) ).join(); responses.forEach((request, responseFuture) -> { LOGGER.info(“Async response: ” + responseFuture.getNow(null)); }); HTTP/2進行了一些必要改進,
使舊的基於文本的協定變得煥然一新,
並拋棄了令人討厭的HTTP/1.1中的很多工作流程,
但是它並未解決所有已知的問題。
結論 從Java 9方面來看,
新的HTTP/2用戶端貌似不錯,
但它的下一個版本才會是合格的產品。
同時,
如果需要HTTP/2支援的話,
上面的庫都可以使用。
來自SSL中國 文章轉載:https://www.trustauth.cn/news/security-news/18897.html