Blogger 站內搜尋

2025年1月18日 星期六

JAVA 7 to 17 Http2

一、前言

一、前言

返回目錄

HTTP/2 作為 HTTP/1.1 的繼任者,在效能、效率以及用戶體驗方面帶來了顯著的提升。它引入了多路複用、頭部壓縮、伺服器推送等關鍵特性,大幅減少了網頁載入時間並優化了資源利用率。HTTP/2 並不僅限於傳輸文本數據,它也能高效地傳輸二進制資料,例如圖像、音頻和影片等。然而,Java 在對 HTTP/2 的支援上並非一蹴而就,而是在不同版本中逐步完善。 值得注意的是,Java 原生 API (例如 java.net.http 包) 主要提供了 HTTP/2 客戶端 (Client) 的支援,並沒有提供直接構建 HTTP/2 伺服器 (Server) 的方式。 因此,在 Java 中建立 HTTP/2 伺服器通常需要依賴第三方函式庫,例如 Netty。本文將深入探討 Java 7 至 17 各版本在 HTTP/2 方面的發展,分析其關鍵特性、架構、相關工具以及具體範例,幫助讀者理解 Java 如何逐步擁抱並應用 HTTP/2,以及如何透過第三方函式庫建立 HTTP/2 伺服器。

目錄



二、Java 7/8:HTTP/2 的萌芽

返回目錄

在 Java 7 和 8 中,官方標準庫並未直接提供 HTTP/2 的原生支援。當時,如果開發者需要使用 HTTP/2,通常需要依賴第三方函式庫,例如:

  • Netty: 一個高度可擴展的非同步事件驅動網路應用框架,提供了強大的 HTTP/2 支援。
  • Jetty: 一個輕量級的 Java HTTP 伺服器和 Servlet 容器,通過其 HTTP/2 模組可以支援 HTTP/2。

這時期使用 HTTP/2 往往需要額外的依賴和配置,使得應用程式的部署和維護成本較高。儘管如此,這也為後續 Java 版本原生支援 HTTP/2 打下了基礎。

架構圖(基於第三方函式庫):

客戶端

第三方函式庫: Netty/Jetty

HTTP/2 伺服器

範例 (使用 Netty - 簡化):

(範例僅供示意,實際程式碼較為複雜,需參考 Netty 文件)

三、Java 9:HTTP/2 客戶端 API 的曙光

返回目錄

Java 9 引入了新的 HTTP Client API ,這是一個重要的里程碑。這個 API 提供了一個現代化且更為易用的 HTTP 客戶端,同時,它也提供了 HTTP/2 的初步支援。雖然 Java 9 的 HTTP Client API 仍處於孵化階段 (Incubator Module),但它展現了 Java 對 HTTP/2 的積極擁抱。

架構圖(Java 9 HTTP Client API):

客戶端

Java 9 HTTP Client API

HTTP/2 伺服器

工具:

  • java.net.http 相關的類別,例如 HttpClient , HttpRequest , HttpResponse

範例 (Java 9 HTTP Client API - 簡化):

注意: Java 9的HTTP Client API在最初是incubator module, 後續版本才正式成為標准API

四、Java 11:HTTP Client API 的正式加入

返回目錄

Java 11 正式移除了 Incubator Module,將 Java 9 引入的 HTTP Client API 納入標準 API。這意味著開發者可以無需擔心 API 的變動,直接使用 java.net.http 包下的類別進行 HTTP/2 通訊。這大大簡化了開發流程,降低了程式碼的複雜度。Java 11 的 HTTP Client API 支援同步與非同步請求,提供了完善的錯誤處理機制,並可配合 TLS (Transport Layer Security) 使用,安全高效。

架構圖(Java 11 HTTP Client API):

客戶端

Java 11 HTTP Client API

HTTP/2 伺服器

工具:

  • java.net.http 包中的 HttpClient , HttpRequest , HttpResponse , WebSocket 等類別。

範例 (Java 11 HTTP Client API - 同步請求): (與 Java 9 範例類似,但使用正式 API)

範例 (Java 11 HTTP Client API - 非同步請求):

五、HTTP Server 和 HTTP/2 Client 範例

返回目錄

本節使用 Java 內建的 com.sun.net.httpserver.HttpsServer 來建立一個簡單的 HTTPS 伺服器,並使用 java.net.http.HttpClient 建立 HTTP/2 客戶端。 這裡需要 JDK 11 以上的版本才能執行。

資料夾目錄:

說明:

  • HTTPS (Hypertext Transfer Protocol Secure): HTTPS 是 HTTP 的安全版本,它通過 TLS/SSL 協定加密客戶端和伺服器之間的通訊,保障資料傳輸的安全性。為了建立 HTTPS 連線,伺服器需要一個憑證。
  • 自簽憑證: 為了簡化開發流程,範例使用了自簽憑證。自簽憑證是由伺服器自己生成的憑證,而不是由受信任的憑證頒發機構 (CA) 簽發。在生產環境中,應該使用 CA 簽發的憑證。
  • com.sun.net.httpserver 這是 Java SE 提供的一個輕量級 HTTP 伺服器 API。它可以用於快速建立簡單的 HTTP 伺服器,但功能相對有限,不適用於大型或高負載的應用程式。
  • java.net.http.HttpClient 這是 Java 9 中引入的標準 HTTP 客戶端 API。它支援 HTTP/1.1 和 HTTP/2,提供了同步和非同步請求等功能。
  • HTTP/2: HTTP/2 是 HTTP/1.1 的改進版本,具有多路複用、頭部壓縮等特性,可以提高網頁載入速度和效能。

程式碼結構: 這個範例程式碼被拆分為幾個檔案

(5.1) 使用 Bouncy Castle 建立自簽憑證

SelfSignedCertificateGenerator.java

  • 功能: 使用 Bouncy Castle 函式庫生成自簽憑證和相關的金鑰儲存 (KeyStore) 檔案。
  • 說明:
    • generateRSAKeyPair(int keySize) : 生成 RSA 金鑰對。
    • createCertificate(KeyPair keyPair, String dnString, int validityDays) : 使用金鑰對,產生自簽憑證。
    • saveCert(String aliasName, X509Certificate cert, PrivateKey key, char[] keyStorePassword) : 將憑證與私鑰儲存到金鑰儲存檔案中。
  • Bouncy Castle: 這是一個第三方加密庫,用於生成自簽憑證證書。

下載 Bouncy Castle 第三方套件: Bouncy Castle 官方網站

(5.2) 使用原生 JAVA SE API 建立 Local Server

SimpleHttpServer.java

  • 功能: 建立一個簡單的 HTTPS 伺服器,監聽 8443 端口。
  • 說明:
    • 首先,使用 SelfSignedCertificateGenerator 生成自簽憑證與金鑰,並載入到 KeyStore 中。
    • 接著,使用 SSLContext 設定 TLS/SSL 連線所需的憑證和密碼。
    • 使用 HttpsServer.create() 建立一個 HttpsServer 物件,並設定 HttpsConfigurator 來配置 SSL。
    • 使用 createContext() 設定請求處理邏輯,這裡只簡單返回 Hello, HTTP from Simple Server. 的文字訊息。
    • 伺服器處理請求時,會輸出 HTTP 版本和請求 Header。

(5.3) 使用原生 JAVA SE API 建立創建一個 Http2 Client

SimpleHttp2Client.java

  • 功能: 建立一個 HTTP/2 客戶端,發送請求到 https://localhost:8443
  • 說明:
    • 建立 HttpClient 物件時,使用 version(HttpClient.Version.HTTP_2) 強制使用 HTTP/2。
    • 為了避免自簽憑證造成連線錯誤,使用了 TrustManager 來信任所有憑證 (這僅限測試用途, 在生產環境中絕對不要這樣做 )。
    • 發送請求後,輸出伺服器的回應 body 和 headers。

(5.4) 讓我們執行以上範例

編譯 SelfSignedCertificateGenerator.java :

啟動 Local Server [1]

執行 Client [1:1]

使用 curl 執行請求

Server 端顯示的資訊

(5.5) Http Server 不支援 HTTP/2

即使你嘗試使用 HttpClient .version(HttpClient.Version.HTTP_2) 或 curl -http2 參數來發送請求,伺服器端仍然顯示 HTTP/1.1 的原因,很可能是客戶端與伺服器之間 TLS 協商的結果,導致雙方最後選擇使用 HTTP/1.1。

  • 問題分析:ALPN (Application-Layer Protocol Negotiation) 缺失
    • HTTP/2 的 TLS 連線需要使用 ALPN 擴展,讓客戶端和伺服器在 TLS 握手階段協商應用層協議。如果你的 Java HTTP Server 沒有正確配置 ALPN,它將無法宣告支援 HTTP/2。
    • 由於你使用的是 Java 內建的 com.sun.net.httpserver.HttpsServer,它預設並不支援 ALPN。
    • 即使客戶端程式碼 (例如 curl -http2 或 Java 內建的 HTTP Client) 要求使用 HTTP/2,但如果伺服器端不支援 ALPN,協商的結果仍會是 HTTP/1.1。

六、使用第三方套件建立 HTTP/2 伺服器

返回目錄

本節將使用 Netty 這個高效能的非同步網路應用程式框架來建立一個支援 HTTP/2 的伺服器。 Netty 提供了對 HTTP/2 的良好支援,並具有非同步、事件驅動、高性能等優點。

下載 Netty 第三方套件: Netty

如果您使用 bash ,以下兩個 jar 檔案需要下載:

資料夾目錄:

Netty 的核心概念

在深入程式碼之前,我們先來了解一下 Netty 的幾個核心概念,這有助於我們理解程式碼的結構:

  • EventLoopGroup: 負責處理網路事件的執行緒池,Netty 使用非同步 I/O,它包含一或多個 EventLoop ,每個 EventLoop 負責處理一個或多個 Channel 上的 I/O 事件。
    • 在我們的範例中, bossGroup 負責接收新的連線, workerGroup 負責處理連線上的 I/O。
  • Channel: 表示一個網路連線的抽象,提供讀取、寫入、關閉等操作。
    • NioServerSocketChannel 是 Netty 用於接收新的 TCP 連線的 Channel 實現。
  • ChannelPipeline: Netty 的處理器鍊,資料會在 ChannelPipeline 中的處理器 ( ChannelHandler ) 之間傳遞。
    • 每個 ChannelHandler 可以修改、過濾、攔截、處理資料。
  • ChannelHandler: Netty 中處理 I/O 事件的元件,例如 SslHandler 處理 SSL/TLS 加密, LoggingHandler 輸出日誌。
    • 我們自訂的 SimpleHttp2ServerHandler 是一個 ChannelHandler ,用於處理 HTTP/2 請求。
  • Http2FrameCodec: Netty 提供的編碼器和解碼器,將底層的二進位資料轉換為 HTTP/2 框架,方便上層的 Handler 處理。
  • Http2MultiplexHandler: Netty 提供的處理器,用於處理多個 HTTP/2 串流,它將同一個連線上的多個串流分發到不同的 Channel 上處理。
  • ALPN (Application-Layer Protocol Negotiation): TLS 的一個擴展,允許客戶端和伺服器在 TLS 握手階段協商使用哪種應用層協定 (例如 HTTP/1.1 或 HTTP/2)。

範例: HTTP/2 Server

SimpleHttp2Server.java

  • 功能: 使用 Netty 框架建立一個支援 HTTP/2 的 HTTPS 伺服器。
  • 說明:
    • 使用 SelfSignedCertificate 生成自簽憑證,用於 HTTPS 連線。
    • 透過 SslContextBuilder 配置 SSL 上下文,並啟用 ALPN 來協商 HTTP/2 協議。
    • 使用 ServerBootstrap 設定 Netty 伺服器,包括設定 EventLoopGroup 和 Channel 類型。
    • 透過 ChannelInitializer 初始化 Channel Pipeline,加入 SslHandler、LoggingHandler、Http2FrameCodec 和 Http2MultiplexHandler 等處理器。
    • Http2FrameCodec 負責編碼和解碼 HTTP/2 框架,Http2MultiplexHandler 負責處理多個 HTTP/2 串流。
    • 使用自訂的 SimpleHttp2ServerHandler 處理 HTTP/2 請求,並回覆簡單的 "Hello World" 訊息。
  • Netty : 一個高性能、非同步事件驅動的網路應用程式框架,用於快速開發高效能的網路伺服器和客戶端。
  • ALPN (Application-Layer Protocol Negotiation) : TLS 的一個擴展,允許客戶端和伺服器在 TLS 握手階段協商使用哪種應用層協定 (例如 HTTP/1.1 或 HTTP/2)。

啟動伺服器:

執行 Client

使用 curl 執行請求

Server 端顯示的資訊

Netty官方有提供不少的範例可供學習,例如: http2-helloworld-server ,在pipeline中使用了升級 UpgradeCodecFactory 的方式處理 HTTP/1.1 與 HTTP/2,有興趣可以下載源碼執行看看。

七、Java 12 ~ 17:持續的改進與優化

返回目錄

Java 12 至 17 主要在 HTTP Client API 的基礎上進行了改進和優化, 這些改進主要集中在性能、穩定性和易用性方面,而非核心架構的變動。

  • 性能優化: 在非同步請求處理方面進行了性能提升,使得處理大量並行請求時,程式的效率更高,資源利用率也會更好。
  • bug 修復: 針對先前版本發現的 bug 進行了修復,提高了 API 的穩定性和可靠性。
  • 新的特性:
    • 增加了對 WebSocket 的支援 (Java 11 起) 以及後續版本的完善。
    • 其他細節性的改進,例如 API 的易用性、錯誤處理機制、以及對特定邊緣情況的處理等方面的提升,使得 HTTP Client API 更為穩定和易用。

在 Java 12 到 17 期間,HTTP Client API 的核心功能和架構沒有發生重大改變,主要是針對細節方面進行了完善,因此開發者可以繼續使用 java.net.http 包下的類別來進行 HTTP/2 通訊,並享受性能提升和 bug 修復帶來的好處,而無需擔心 API 的不兼容性問題。

架構圖 (Java 12-17 HTTP Client API):

客戶端

Java 12-17 HTTP Client API

HTTP/2 伺服器

八、總結

返回目錄

從 Java 7 的第三方函式庫到 Java 17 的原生支援,Java 在 HTTP/2 的發展過程中走過了漫長的道路。Java 9 引入的 HTTP Client API 作為關鍵的轉折點,而 Java 11 將其納入標準 API,使得開發者可以更便捷、高效地使用 HTTP/2。 Java 12 至 17 在此基礎上進行了持續的優化和改進,主要集中在性能、穩定性和易用性方面,而非核心架構的變動。 這使得 API 的整體表現更為出色,開發者可以更放心地使用 java.net.http 包進行 HTTP/2 通訊。

總結來說,Java 對 HTTP/2 的支援是一個逐步發展的過程,從最初依賴第三方函式庫,到後來提供原生的客戶端 API,再到後續的持續改進,每一步都為開發者提供了更為便捷高效的工具。 值得注意的是,Java 原生 API 僅提供 HTTP/2 Client 的支援,若要建立 HTTP/2 Server 仍需要依賴第三方套件 (如 Netty)。 開發者應當充分利用這些新特性,以及如 Netty 這類的第三方函式庫,構建更加現代化和高效的網路應用程式。

返回目錄

附錄

返回目錄


  1. JEP 330(Launch Single-File Source-Code Programs)是 Java 11 引入的一個重要特性,它允許開發者直接使用 java 指令執行單一檔案的 Java 原始碼( .java 檔案),而無需事先編譯成 .class 檔案。 ↩︎ ↩︎

沒有留言:

張貼留言