當前位置:
首頁 > 最新 > 一篇文章帶你徹底了解HTTP 2.0

一篇文章帶你徹底了解HTTP 2.0

原文地址:點擊左下角閱讀全文跳轉至原文

HTTP 2.0是在SPDY(An experimental protocol for a faster web, The Chromium Projects)基礎上形成的下一代互聯網通信協議。HTTP/2 的目的是通過支持請求與響應的多路復用來較少延遲,通過壓縮HTTPS首部欄位將協議開銷降低,同時增加請求優先順序和伺服器端推送的支持。

本文目的是學習HTTP 2.0的原理並研究其通信的詳細細節。大部分知識點源於《Web性能權威指南》。


1. 二進位分幀層

二進位分幀層,是HTTP 2.0性能增強的核心。

HTTP 1.x在應用層以純文本的形式進行通信,而HTTP 2.0將所有的傳輸信息分割為更小的消息和幀,並對它們採用二進位格式編碼。這樣,客戶端和服務端都需要引入新的二進位編碼和解碼的機制。

如下圖所示,HTTP 2.0並沒有改變HTTP 1.x的語義,只是在應用層使用二進位分幀方式傳輸。

因此,也引入了新的通信單位:


HTTP 2.0通信的最小單位,包括幀首部、流標識符、優先值和幀凈荷等。

其中,幀類型又可以分為:

DATA:用於傳輸HTTP消息體;

HEADERS:用於傳輸首部欄位;

SETTINGS:用於約定客戶端和服務端的配置數據。比如設置初識的雙向流量控制窗口大小;

WINDOW_UPDATE:用於調整個別流或個別連接的流量

PRIORITY: 用於指定或重新指定引用資源的優先順序。

RST_STREAM: 用於通知流的非正常終止。

PUSH_ PROMISE: 服務端推送許可。

PING: 用於計算往返時間,執行「 活性」 檢活。

GOAWAY: 用於通知對端停止在當前連接中創建流。

標誌位用於不同的幀類型定義特定的消息標誌。比如DATA幀就可以使用表示該條消息通信完畢。流標識位表示幀所屬的流ID。優先值用於HEADERS幀,表示請求優先順序。R表示保留位。

下面是Wireshark抓包的一個DATA幀:


消息是指邏輯上的HTTP消息(請求/響應)。一系列數據幀組成了一個完整的消息。比如一系列DATA幀和一個HEADERS幀組成了請求消息。


流是連接中的一個虛擬信道,可以承載雙向消息傳輸。每個流有唯一整數標識符。為了防止兩端流ID衝突,客戶端發起的流具有奇數ID,伺服器端發起的流具有偶數ID。

所有HTTP 2. 0 通信都在一個TCP連接上完成, 這個連接可以承載任意數量的雙向數據流Stream。 相應地, 每個數據流以 消息的形式發送, 而消息由一 或多個幀組成,這些幀可以亂序發送, 然後根據每個幀首部的流標識符重新組裝。

二進位分幀層保留了HTTP的語義不受影響,包括首部、方法等,在應用層來看,和HTTP 1.x沒有差別。同時,所有同主機的通信能夠在一個TCP連接上完成。


2. 多路復用共享連接

基於二進位分幀層,HTTP 2.0可以在共享TCP連接的基礎上,同時發送請求和響應。HTTP消息被分解為獨立的幀,而不破壞消息本身的語義,交錯發送出去,最後在另一端根據流ID和首部將它們重新組合起來。

我們來對比下HTTP 1.x和HTTP 2.0,假設不考慮1.x的pipeline機制,雙方四層都是一個TCP連接。客戶端向服務度發起三個圖片請求/image1.jpg,/image2.jpg,/image3.jpg。

HTTP 1.x發起請求是串列的,image1返回後才能再發起image2,image2返回後才能再發起image3。

HTTP 2.0建立一條TCP連接後,並行傳輸著3個數據流,客戶端向服務端亂序發送stream1~3的一系列的DATA幀,與此同時,服務端已經在返回stream 1的DATA幀

性能對比,高下立見。HTTP 2.0成功解決了HTTP 1.x的隊首阻塞問題(TCP層的阻塞仍無法解決),同時,也不需要通過pipeline機制多條TCP連接來實現並行請求與響應。減少了TCP連接數對伺服器性能也有很大的提升。


3. 請求優先順序

流可以帶有一個31bit的優先順序:

0:表示最高優先順序

231-1:表示最低優先順序

客戶端明確指定優先順序,服務端可以根據這個優先順序作為依據交互數據,比如客戶端優先順序設置為.css>.js>.jpg(具體可參見《高性能網站建設指南》), 服務端按優先順序返回結果有利於高效利用底層連接,提高用戶體驗。

然而,也不能過分迷信請求優先順序,仍然要注意以下問題:

服務端是否支持請求優先順序

會否引起隊首阻塞問題,比如高優先順序的慢響應請求會阻塞其他資源的交互。


4. 服務端推送

HTTP 2.0增加了服務端推送功能,服務端可以根據客戶端的請求,提前返回多個響應,推送額外的資源給客戶端。如下圖所示,客戶端請求stream 1,/page.html。服務端在返回stream 1消息的同時推送了stream 2(/script.js)和stream 4(/style.css)。

PUSH_PROMISE幀是服務端向客戶端有意推送資源的信號。

如果客戶端不需要服務端Push,可在SETTINGS幀中設定服務端流的值為0,禁用此功能

PUSH_PROMISE幀中只包含預推送資源的首部。如果客戶端對PUSH_PROMISE幀沒有意見,服務端在PUSH_PROMISE幀後發送響應的DATA幀開始推送資源。如果客戶端已經緩存該資源,不需要再推送,可以選擇拒絕PUSH_PROMISE幀。

PUSH_PROMISE必須遵循請求-響應原則,只能借著對請求的響應推送資源。

目前,Apache的能夠開啟 服務端推送Push。Nginx的還不支持服務端Push。

HTTP 1.x每一次通信(請求/響應)都會攜帶首部信息用於描述資源屬性。HTTP 2.0在客戶端和服務端之間使用「首部表」來跟蹤和存儲之前發送的鍵-值對。首部表在連接過程中始終存在,新增的鍵-值對會更新到表尾,因此,不需要每次通信都需要再攜帶首部。

另外,HTTP 2.0使用了首部壓縮技術,壓縮演算法使用HPACK。可讓報頭更緊湊,更快速傳輸,有利於移動網路環境。

需要注意的是,HTTP 2.0關注的是首部壓縮,而我們常用的gzip等是報文內容(body)的壓縮。二者不僅不衝突,且能夠一起達到更好的壓縮效果。


5. 一個完整的HTTP 2.0通信過程

考慮一個問題,客戶端如何知道服務端是否支持HTTP 2.0?是否支持對二進位分幀層的編碼和解碼?所以,在兩端使用HTTP 2.0通信之前,必然存在協議協商的過程。


支持HTTP 2.0的瀏覽器可以在TLS會話層自發完成和服務端的協議協商以確定是否使用HTTP 2.0通信。其原理是TLS 1.2中引入了擴展欄位,以允許協議的擴展,其中ALPN協議(Application Layer Protocol Negotiation, 應用層協議協商, 前身是NPN)用於客戶端和服務端的協議協商過程。

服務端使用ALPN,監聽443埠默認提高HTTP 1.1,並允許協商其他協議,比如SPDY和HTTP 2.0。

比如,客戶端在TLS握手Client Hello階段表明自身支持HTTP 2.0

服務端收到後,響應Server Hello,表示自己也支持HTTP 2.0。雙方開始HTTP 2.0通信。


然而,HTTP 2.0一定是HTTPS(TLS 1.2)的特權嗎?

當然不是,客戶端使用HTTP也可以開啟HTTP 2.0通信。只不過因為HTTP 1. 0和HTTP 2. 0都使用同一個 埠(80), 又沒有伺服器是否支持HTTP 2. 0的其他任何 信息,此時 客戶端只能使用HTTP Upgrade機制(OkHttp, nghttp2等組件均可實現,也可以自己編碼完成)通過協調確定適當的協議:


TCP連接建立:

TLS握手和HTTP 2.0通信過程:

另外,在chrome中通過命令也能捕獲HTTP 2.0通信過程:


6. HTTP 2.0性能瓶頸

是不是啟用HTTP 2.0後性能必然提升了?任何事情都不是絕對的,雖然總體而言性能肯定是能提升的。

我想HTTP 2.0會帶來新的性能瓶頸。因為現在所有的壓力集中在底層一個TCP連接之上,TCP很可能就是下一個性能瓶頸,比如TCP分組的隊首阻塞問題,單個TCP packet丟失導致整個連接阻塞,無法逃避,此時所有消息都會受到影響。未來,伺服器端針對HTTP 2.0下的TCP配置優化至關重要,有機會我們再跟進詳述。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 java版web項目 的精彩文章:

TAG:java版web項目 |