HTTP/2 新特性淺析

HTTP/2 源自 SPDY/2

SPDY 系列協議由谷歌開發,於 2009 年公開。它的設計目標是下降 50% 的頁面加載時間。當下不少著名的互聯網公司,例如百度、淘寶、UPYUN 都在本身的網站或 APP 中採用了 SPDY 系列協議(當前最新版本是 SPDY/3.1),由於它對性能的提高是顯而易見的。主流的瀏覽器(谷歌、火狐、Opera)也都早已經支持 SPDY,它已經成爲了工業標準,HTTP Working-Group 最終決定以 SPDY/2 爲基礎,開發 HTTP/2。html

可是,HTTP/2 跟 SPDY 仍有不一樣的地方,主要是如下兩點:git

  1. HTTP/2 支持明文 HTTP 傳輸,而 SPDY 強制使用 HTTPS
  2. HTTP/2 消息頭的壓縮算法採用 HPACK,而非 SPDY 採用的 DELEFT

HTTP/2 的優點

相比 HTTP/1.x,HTTP/2 在底層傳輸作了很大的改動和優化:github

  1. HTTP/2 採用二進制格式傳輸數據,而非 HTTP/1.x 的文本格式。二進制格式在協議的解析和優化擴展上帶來更多的優點和可能。
  2. HTTP/2 對消息頭採用 HPACK 進行壓縮傳輸,可以節省消息頭佔用的網絡的流量。而 HTTP/1.x 每次請求,都會攜帶大量冗餘頭信息,浪費了不少帶寬資源。頭壓縮可以很好的解決該問題。
  3. 多路複用,直白的說就是全部的請求都是經過一個 TCP 鏈接併發完成。HTTP/1.x 雖然經過 pipeline 也能併發請求,可是多個請求之間的響應會被阻塞的,因此 pipeline 至今也沒有被普及應用,而 HTTP/2 作到了真正的併發請求。同時,流還支持優先級和流量控制。
  4. Server Push:服務端可以更快的把資源推送給客戶端。例如服務端能夠主動把 JS 和 CSS 文件推送給客戶端,而不須要客戶端解析 HTML 再發送這些請求。當客戶端須要的時候,它已經在客戶端了。

HTTP/2 主要是 HTTP/1.x 在底層傳輸機制上的徹底重構,HTTP/2 是基本兼容 HTTP/1.x 的語義的(詳細兼容性說明請戳 這裏)。Content-Type 仍然是 Content-Type,只不過它再也不是文本傳輸了。那麼 HTTP/2 的這些新特性又是如何實現的呢?算法

HTTP/2 的基石 - Frame

Frame 是 HTTP/2 二進制格式的基礎,基本能夠把它理解爲它 TCP 裏面的數據包同樣。HTTP/2 之因此可以有如此多的新特性,正是由於底層數據格式的改變。 Frame 的基本格式以下(圖中的數字表示所佔位數,內容摘自 http2-draft-17):瀏覽器

+-----------------------------------------------+
|                 Length (24)                   |
+---------------+---------------+---------------+
|   Type (8)    |   Flags (8)   |
+-+-------------+---------------+-------------------+
|R|                 Stream Identifier (31)          |
+=+=================================================+
|                   Frame Payload (0...)        ...
+---------------------------------------------------+
  • Length: 表示 Frame Payload 部分的長度,另外 Frame Header 的長度是固定的 9 字節(Length + Type + Flags + R + Stream Identifier = 72 bit)。
  • Type: 區分這個 Frame Payload 存儲的數據是屬於 HTTP Header 仍是 HTTP Body;另外 HTTP/2 新定義了一些其餘的 Frame Type,例如,這個字段爲 0 時,表示 DATA 類型(即 HTTP/1.x 裏的 Body 部分數據)
  • Flags: 共 8 位, 每位都起標記做用。每種不一樣的 Frame Type 都有不一樣的 Frame Flags。例如發送最後一個 DATA 類型的 Frame 時,就會將 Flags 最後一位設置 1(flags &= 0x01),表示 END_STREAM,說明這個 Frame 是流的最後一個數據包。
  • R: 保留位。
  • Stream Identifier: 流 ID,當客戶端和服務端創建 TCP 連接時,就會先發送一個 Stream ID = 0 的流,用來作些初始化工做。以後客戶端和服務端從 1 開始發送請求/響應。

Frame 由 Frame Header 和 Frame Payload 兩部分組成。不管是原來的 HTTP Header 仍是 HTTP Body,在 HTTP/2 中,都將這些數據存儲到 Frame Payload,組成一個個 Frame,再發送響應/請求。經過 Frame Header 中的 Type 區分這個 Frame 的類型。因而可知語義並無太大變化,而是數據的格式變成二進制的 Frame。兩者的轉換和關係以下圖:緩存

HTTP/1.x 數據轉換爲 HTTP/2
圖片引用自這裏服務器

爲 HTTP/2 頭壓縮專門設計的 HPACK

若是咱們約定將經常使用的請求好比 GET /index.html 用一個 1 來表示,POST /index.html 用 2 來表示。那麼是否是能夠節省不少字節?網絡

爲 HTTP/2 的專門量身打造的 HPACK 即是相似這樣的思路延伸。它使用一份索引表來定義經常使用的 HTTP Header。把經常使用的 HTTP Header 存放在表裏。請求的時候便只須要發送在表裏的索引位置便可。例如 :method=GET 使用索引值 2 表示,:path=/index.html 使用索引值 5 表示(完整的列表參考:HPACK Static Table)。只要給服務端發送一個 Frame,該 Frame 的 Payload 部分存儲 0x8285,Frame 的 Type 設置爲 Header 類型,即可表示這個 Frame 屬於 HTTP Header,請求的內容是:併發

GET /index.html

爲何是 0x8285,而不是 0x0205? 這是由於高位設置爲 1 表示這個字節是一個徹底索引值(key 和 value 都在索引中)。相似的,經過高位的標誌位能夠區分出這個字節是屬於一個徹底索引值,仍是僅索引了 key,仍是 key 和 value 都沒有索引。由於索引表的大小的是有限的,它僅保存了一些經常使用的 HTTP Header,同時每次請求還能夠在表的末尾動態追加新的 HTTP Header 緩存。動態部分稱之爲 Dynamic Table。Static Table 和 Dynamic Table 在一塊兒組合成了索引表:性能

<----------  Index Address Space ---------->
<-- Static  Table -->  <-- Dynamic Table -->
+---+-----------+---+  +---+-----------+---+
| 1 |    ...    | s |  |s+1|    ...    |s+k|
+---+-----------+---+  +---+-----------+---+
                       ^                   |
                       |                   V
                 Insertion Point      Dropping Point

HPACK 不只僅經過索引鍵值對來下降數據量,同時還會將字符串進行霍夫曼編碼來壓縮字符串大小。

以經常使用的 User-Agent 爲例,它在靜態表中的索引值是 58,它的值是不存在表中的,由於它的值是多變的。第一次請求的時候它的 key 用 58 表示,表示這是一個 User-Agent ,它的值部分會進行霍夫曼編碼(若是編碼後的字符串變動長了,則不採用霍夫曼編碼)。服務端收到請求後,會將這個 User-Agent 添加到 Dynamic Table 緩存起來,分配一個新的索引值。客戶端下一次請求時,假設上次請求User-Agent的在表中的索引位置是 62, 此時只須要發送 0xBE(一樣的,高位置 1),即可以表明: User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36。其過程以下圖所示:

HPACK 原理圖
圖片引用自這裏

最終,相同的 Header 只須要發送索引值,新的 Header 會從新加入 Dynamic Table。

Multipexing 多路複用

每一個 Frame Header 都有一個 Stream ID 就是被用於實現該特性。每次請求/響應使用不一樣的 Stream ID。就像同一個 TCP 連接上的數據包經過 IP:PORT來區分出數據包去往哪裏同樣。經過 Stream ID 標識,全部的請求和響應均可以歡快的同時跑在一條 TCP 連接上了。 下圖是 http 和 spdy(http2 的模型和 spdy 是相似的) 的併發模型對比:

http併發模型

當流併發時,就會涉及到流的優先級和依賴。優先級高的流會被優先發送。圖片請求的優先級要低於 CSS 和 SCRIPT,這個設計能夠確保重要的東西能夠被優先加載完。

Server Push

當服務端須要主動推送某個資源時,便會發送一個 Frame Type 爲 PUSH_PROMISE 的 Frame,裏面帶了 PUSH 須要新建的 Stream ID。意思是告訴客戶端:接下來我要用這個 ID 向你發送東西,客戶端準備好接着。客戶端解析 Frame 時,發現它是一個 PUSH_PROMISE 類型,便會準備接收服務端要推送的流。

結束語

本文簡化了不少 HTTP/2 協議中的具體細節,只描述了 HTTP/2 中主要特性實現的基本過程。

若是你想實現一個支持 HTTP/2 的服務器,那麼你能夠移步 HTTP/2 官網 作更多瞭解,它還提供了一份已經實現 HTTP/2 的項目列表:https://github.com/http2/http...

另外,關於 HTTP/2 性能如何,能夠參考官方小組給出的例子:https://http2.akamai.com/demo

UPYUN 在不久的未來也會加入對 HTTP/2 協議支持,爲用戶提供更好更快的雲加速服務。

追加:目前又拍雲已全網支持 HTTP/2 協議及 SPDY3.1協議。

又拍雲 CDN 當前已全平臺支持 HTTP/2,並已默認開啓。又因 HTTP/2 是在 HTTPS 協議的基礎上實現的,因此只要使用又拍雲 HTTPS 加速服務的域名,均可免費享受 HTTP/2 服務,無需作任何特殊配置。而開啓HTTPS,只需完成證書申請與管理,無須繁雜流程,輕鬆實現網站與 Web 應用的 HTTPS 加密部署。

延伸閱讀

相關文章
相關標籤/搜索