先說結論:HTTP/2的“長連接” = 一個TCP連接 + 多路復用 + 二進制幀 + 流控制 + 持久會話管理
它不只是“連接不斷”,更關鍵的是:在這個長連接上,可以同時并發傳輸成百上千個請求和響應,互不阻塞!
1、HTTP/2的“長連接”從哪開始?
和HTTP/1.1一樣,HTTP/2也是基于TCP協議的。
首先會做的事情和HTTP1.1還是一樣的,如:
1、客戶端(比如瀏覽器)和服務器建立TCP連接
2、然后進行TLS握手(如果是HTTPS,可選)
握手完成后,客戶端發送一個特殊的“連接前綴”(Connection Preface),如:
"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
它的作用是告訴服務器:
“我要用HTTP/2了,別當成HTTP/1.x處理。”
從此刻起,這個TCP連接就被“升級”為一個HTTP/2連接,并期望長期保持。
2、核心機制:多路復用(Multiplexing),讓一個連接“變寬”
(1)、HTTP/1.1的問題(串行排隊)
在HTTP/1.1中,即使有keep-alive,也只能:
- 發請求A → 等響應A → 發請求B → 等響應B → …
- 或者開6個TCP連接來并發(浪費資源)
這就像是:一條單車道公路,雖然可以供多輛汽車經過,但汽車只能一輛一輛走。
主要問題:
- 隊頭阻塞(Head-of-Line Blocking):HTTP/1.1的每個請求必須串行發送(一個接一個),前一個請求未完成時,后續請求必須排隊等待。例如,加載網頁時,如果圖片A的請求卡住了,CSS和JS的請求也會被堵住。
- 頻繁建立連接:為了并行請求,瀏覽器通常會建立多個TCP連接(如6-8個),但頻繁的連接建立和關閉會消耗資源。
(2)、HTTP/2的解決方案:多路復用
HTTP/2把所有請求和響應拆成小塊(幀),然后在同一個TCP連接上交錯發送。
這就像是: HTTP/2將一個TCP連接想象成一條多車道公路,每個請求和響應是不同的車輛(數據流),它們可以并行行駛(并發傳輸),互不干擾。
- 每個請求/響應對(如一個圖片請求和它的響應)被分配一個唯一的Stream ID(流標識符),接收方通過這個ID將數據重新組裝。
- 示例:
- 請求1(圖片A):Stream ID = 1
- 請求2(CSS文件):Stream ID = 3
- 請求3(JS文件):Stream ID = 5
- 這些請求的數據幀可以混合在同一個TCP連接上傳輸,服務器根據Stream ID分別處理。
效果:
- 單連接并發請求和處理:一個TCP連接可以同時處理數十甚至上百個請求,無需反復建立連接。
- 消除隊頭阻塞:即使某個請求的數據包丟失,其他請求的數據仍能繼續傳輸。
3、關鍵技術:二進制分幀層(Binary Framing Layer)
這是實現多路復用的底層基礎。
1、消息結構分層
HTTP/2把通信分成三層:
2、什么是Frame(幀)?
HTTP/1.1的文本格式問題:
- HTTP/1.1的請求和響應使用純文本格式(如GET /index.html HTTP/1.1),解析效率低,且冗余信息多(如重復的頭部字段)。
HTTP/2的二進制分幀:
- 數據切片:HTTP/2將每個請求/響應拆分成多個幀(Frame),每個幀是二進制格式的小數據塊。
幀有種類:
- HEADERS幀:存放請求頭或響應頭(如Host、User-Agent)
- DATA幀:存放請求體或響應體(如 HTML 內容)。
- SETTINGS幀:配置參數
- PING幀:心跳探測
- RST_STREAM幀:取消某個流
- GOAWAY幀:優雅關閉連接
每個幀的格式像這樣:
注意:Stream ID它決定了這個幀屬于哪個“對話”。
示例:
- 請求GET /image.jpg會被拆分為:
1、HEADERS幀(包含Host: example.com等頭部)。
2、DATA幀(可能分多個,傳輸圖片的二進制數據)。 - 服務器收到后,根據Stream ID和幀類型重組完整請求。
效果:
- 高效解析:二進制格式比文本解析更快。
- 減少冗余:通過頭部壓縮進一步優化。
3、什么是Stream(流)?
- 一個Stream是客戶端和服務器之間的一個獨立的雙向數據流。
- 每個HTTP請求/響應對應一個或多個Stream。
- 每個Stream有唯一ID:
- 客戶端發起的Stream ID是奇數(1, 3, 5…)
- 服務器發起的(如Server Push)是偶數
舉例:
- 請求首頁:Stream 1
- 請求CSS:Stream 3
- 請求JS:Stream 5
- 請求圖片:Stream 7
這些Stream的幀可以在同一個TCP連接上混在一起發送:
[DATA幀, Stream=3]
[HEADERS幀, Stream=5]
[DATA幀, Stream=1]
[HEADERS幀, Stream=7]
[DATA幀, Stream=3]
接收方根據Stream ID把屬于同一個流的幀重新組裝成完整請求或響應。
這就是多路復用:多個請求/響應并行在一個連接上傳輸,不互相阻塞!
4、頭部壓縮:減少重復信息的傳輸
HTTP/1.1的頭部冗余:
- 每次請求都攜帶大量重復頭部(如User-Agent、Cookie),占總傳輸量的50%以上。
HTTP/2的HPACK壓縮:
- 靜態表:預定義常用頭部字段(如:method: GET、host),用短編碼代替完整字段。
- 動態表:客戶端和服務器維護一個共享的動態表,記錄最近傳輸的頭部值(如cookie: abc123),后續請求只需引用索引。
- 示例:
- 首次請求頭部::method: GET, host: example.com, user-agent: Chrome
- 動態表記錄這些值。
- 后續請求只需發送索引(如:method: 1, host: 2),大幅減少數據量。
- 首次請求頭部::method: GET, host: example.com, user-agent: Chrome
效果:
- 頭部體積減少50%-90%,提升傳輸效率。
5、服務器推送:主動發送資源
傳統方式的延遲:
- 瀏覽器加載HTML后,發現需要CSS/JS文件,再發起新請求,增加往返延遲。
HTTP/2的服務器推送:
- 主動推送:服務器在客戶端請求HTML時,預測客戶端可能需要的資源(如CSS、JS),提前通過PUSH_PROMISE幀推送。
- 示例:
- 客戶端請求index.html。
- 服務器響應HTML內容,并通過PUSH_PROMISE推送style.css和script.js。
- 客戶端收到后,直接從本地緩存或推送數據中獲取資源,無需額外請求。
效果:
- 減少請求次數:關鍵資源提前加載,縮短頁面渲染時間。
6、流量控制與優先級:智能分配帶寬
流量控制:
- 滑動窗口機制:客戶端和服務器通過WINDOW_UPDATE幀動態調整接收窗口大小,防止緩沖區溢出。
- 示例:客戶端通知服務器:“我現在只能接收10KB數據”,服務器按此限制發送。
優先級:
- 權重分配:每個流(請求)可以設置優先級(1-256),服務器優先處理高權重流。
- 依賴關系:通過PRIORITY幀 建立流之間的依賴樹,例如優先加載HTML再加載圖片。
4、HTTP/2是如何“維持”這個長連接的?
1、不主動關閉連接(默認持久)
- 服務器和客戶端都傾向于長期保持這個TCP連接。
- 不像HTTP/1.1那樣容易超時斷開。(斷開連接時間會比HTTP1.1要長)
- 瀏覽器對同一個域名通常只建立1~2個HTTP/2連接,然后一直復用。
2、使用PING幀保活
- 客戶端或服務器可以定時發送PING幀探測連接是否還活著。
- 對方必須回復一個PONG幀。
- 這有點像“心跳包”,防止NAT或防火墻誤殺空閑連接。
3、流控制(Flow Control)防止壓垮對方
- 每個Stream和整個連接都有流控窗口。
- 接收方可以告訴發送方:“我只能接收這么多數據,別發太快。”
- 通過WINDOW_UPDATE幀動態調整。
- 這讓連接更穩定,避免緩沖區溢出。
4、優雅關閉:GOAWAY幀
- 服務器想關閉連接時,不會直接斷開,而是發一個GOAWAY幀。
- 告訴客戶端:“我已經處理到Stream ID=X了,別再發新的請求了。”
- 已經在傳輸的請求可以完成,新請求會建立新連接。
- 這樣不會中斷用戶正在加載的頁面。
5、實際效果:一個連接干了以前6個連接的事
6、什么情況下HTTP/2長連接會斷開?
雖然想“一直連著”,但現實中有這些情況會導致斷開。
斷開連接后,如果還需要通信,會重新建立一個新的HTTP/2連接,和之前那個沒有關系(但Cookie、Token等應用層狀態還在)。
7、HTTP1.1和HTTP2對比
打個比方:
- HTTP/1.1的長連接:像一條單車道高速公路,車可以一輛接一輛快速開,但不能超車。
- HTTP/2的長連接:像一條上百車道的超級高速公路,所有車(請求)可以同時開,各走各的道(Stream),互不干擾。
8、總結
HTTP/2的長連接則通過多路復用 + 二進制幀實現多個并行請求復用一個TCP連接,效率更高。
雖然HTTP/2改進了應用層的并發,但如果底層TCP出現丟包,仍可能導致整個連接阻塞(這是HTTP/3使用QUIC和UDP的原因)。
現代瀏覽器通常對HTTP/1.1使用多個并行連接(如6~8個)來提升并發,而HTTP/2只需一個連接即可達到更高性能。
HTTP2的特性:
向陽前行,Dare To Be!!!