Go 標準庫 HTTP2 協議升級細節
客戶端行為
首次呼叫 Transport.RoundTrip()
-
初始化 Transport 的 TLS ALPN 列表,加入 "h2" ALPN (code)。同時設定 "h2" ALPN 對應的協議升級函式 (code)。
-
建立TCP連線,隨即啟動TLS握手流程 (code)。客戶端傳送給服務端的 TLS 握手訊息,會攜帶步驟1中設定的 "h2" ALPN (code),與服務端協商。
-
服務端接受 "h2" ALPN,同樣在握手訊息中攜帶 "h2" 作為 ALPN 協商的結果,響應給客戶端。客戶端快取此結果 (code)。
-
客戶端根據 ALPN 協商結果——"h2",找回步驟1中設定的協議升級函式,通過此函式生成新的 HTTP2 Transport ,將其儲存到連線的
alt
-
因為 HTTP2 連線可以同時被多個請求複用(通過 HTTP2 Stream),沒有被佔用的問題,直接把它當成是“空閒”連線,放入到連線池中(code)。
-
由於連線的
alt
變數(HTTP2 Transport )不為空,客戶端直接使用此 Transport 傳送 http 請求,繞過了 HTTP1 相關的處理 (code)。 -
HTTP2 Transport 建立一個新的 Stream 傳送請求 (code)。
後續呼叫 Transport.RoundTrip()
-
客戶端在連線池中發現“空閒”狀態的 HTTP2 連線,因為 HTTP2 連線可以同時被多個請求複用(通過 HTTP2 Stream),沒有被佔用的問題。直接使用這個連線,而且無需將它從連線池中移除(仍舊保持“空閒”狀態)。
-
由於連線的
alt
變數(HTTP2 Transport )不為空,客戶端直接使用此 Transport 傳送 http 請求,繞過了 HTTP1 相關的處理 (code)。 -
HTTP2 Transport 建立一個新的 Stream 傳送請求 (code)。
服務端處理
- 服務端啟動時,會初始化所支援的 TLS ALPN 列表,加入 "h2" ALPN (code)。同時設定 "h2" ALPN 對應的協議升級函式 (code)。
- 接受TCP連線,開始TLS握手流程(code)。不出意外,客戶端會在 TLS 握手訊息中提供 "h2" ALPN,服務端根據步驟1提供的所支援的 TLS ALPN 列表,最終 "h2" 會被選擇為協商的結果。服務端快取此結果(
- 服務端基於 ALPN 協商結果——"h2",找回步驟1中設定的協議升級函式,直接通過此函式處理 HTTP 請求,繞過了 HTTP1 相關的處理 (code)。
- 處理 HTTP2 Stream (code)。
結論
- Go 標準庫通過 TLS (HTTPS) 的 ALPN 協商 直接識別處理 HTTP2 流量,沒有走常規 HTTP1 升級 HTTP2 的流程,縮短了連線建立的時間,效能更好。
- Transport 連線池託管 HTTP2 連線的意義不大,對於同一個域名,連線池內通常只有一個 HTTP2 連線,通過在 HTTP2 連線內 建立多個 Stream 來複用連線。