HTTP2基本概念學習筆記

HTTP2 規範在2015年5月正式發佈,至今大多數瀏覽器和服務器已經對此協議提供了支持:

http2-compatibility
(2018-04-09)

作爲一個對 HTTP1.x 進行了加強、補充和完善的更好的協議,值得我們好好的去了解它,然後使用它做出更美妙的事情。

1 過去和現在

HTTP1.1 自從1997年發佈1999年最後一次修改以來,我們已經使用 HTTP1.x 相當長一段時間了,但是隨着近十年互聯網的爆炸式發展,當時協議規定的某些特性,已經無法滿足現代網絡的需求了。

request body

HTTP1.x 有以下幾點致命缺陷:(以瀏覽器至服務器爲例)

  • 協議規定客戶端對同一域的併發連接最多隻能2個(瀏覽器實現一般是2~8個),但是現代網頁平均一個頁面需要加載 40個資源
  • 線頭阻塞(Head of line blocking)問題:同一個連接中的請求,需要一個接一個串行發送和接收
  • 基於文本協議,請求和響應的頭信息非常大,並且無法壓縮。
  • 不能控制響應優先級,必須按照請求順序響應。
  • 只能單向請求,也就是客戶端請求什麼,服務器只能返回什麼。

就是以上問題嚴重影響了現代互聯網信息交互的效率和靈活性,此時更現代更高效的通訊協議 HTTP2 應運而生。

HTTP2 使用了多路複用HPACK頭壓縮流 + 二進制幀流優先級等技術手段解決上述問題。

2 HTTP2

HTTP2 的前身是 SPDY協議(一個 Google 主導推行的應用層協議,作爲對 HTTP1 的增強),第一版草稿就是基於 SPDY3 規範修改制定而來。HTTP2必須在維持原來 HTTP 的範式(不改動 HTTP/1.x 的語義、方法、狀態碼、URI 以及首部字段等等)前提下,實現突破性能限制,改進傳輸性能,實現低延遲和高吞吐量。

HTTP2 的特性包括:

  1. 傳輸內容使用二進制協議
  2. 使用作爲最小傳輸單位
  3. 多路複用
  4. 頭壓縮
  5. 服務器推送
  6. 優先級與依賴性
  7. 可重置
  8. 流量控制
  9. HTTPS rfc 規範並沒有要求 HTTP2 強制使用 TLS,但是目前世界所有瀏覽器和服務器實現都基於 HTTPS 來實現 HTTP2

2.1 二進制

在 HTTP1.x 時代,無論是傳輸內容還是頭信息,都是文本/ASCII編碼的,雖然這有利於直接從請求從觀察出內容,但是卻使得想要實現併發傳輸異常困難(存在空格或其他字符,很難判斷消息的起始和結束)。使用二進制傳輸可以避免這個問題,因爲傳輸內容只有1和0,通過下面第二點的“幀”規範規定格式,即可輕易識別出不同類型內容。同時使用二進制有一個顯而易見的好處是:更小的傳輸體積。

2.2 二進制分幀

HTTP2 在維持原有 HTTP 範式的前提下,實現突破性能限制,改進傳輸性能,實現低延遲和高吞吐量的其中一個關鍵是:在應用層(HTTP2)和傳輸層(TCP or UDP)之間增加了二進制分幀層

frame

幀(Frame)是 HTTP2 通訊中的最小傳輸單位,所有幀以固定的 9 個八位字節頭部開頭,隨後是一個可變長度的有效載荷

幀結構圖
 +-----------------------------------------------+
 |                 長度Length (24)                |
 +---------------+---------------+---------------+
 |   類型Type (8)    |   標誌Flags (8)   |
 +-+-------------+---------------+-------------------------------+
 |R|                 流標識符Stream Identifier (31)               |
 +=+=============================================================+
 |                   幀載荷Frame Payload (0...)                 ...
 +---------------------------------------------------------------+

規範中一共定義了 10 種不同的幀,其中最基礎的兩種分別對應於 HTTP1.x 的 DATA 和 HEADERS。

一個真正的 HTTP2 請求類似下圖:

steam

2.3 多路複用(Multiplexing)和流

上一節提到的 Stream Identifier 將 HTTP2 連接上傳輸的每個幀都關聯到一個“流”。流是一個獨立的,雙向的幀序列,可以通過一個 HTTP2 的連接在服務端與客戶端之間不斷的交換數據。
每個單獨的 HTTP2 連接都可以包含多個併發的流,這些流中交錯的包含着來自兩端的幀。流既可以被客戶端/服務器端單方面的建立和使用,也可以被雙方共享,或者被任意一邊關閉。在流裏面,每一幀發送的順序非常關鍵。接收方會按照收到幀的順序來進行處理。

上面是《HTTP2 講解》對流的解釋,下面接着是一個小火車的例子,但是個人覺得這個例子有一定的偏差,並且並不能讓人直觀的理解 幀-流-連接 之間的關係,以下是個人理解:

A “stream” is an independent, bidirectional sequence of frames exchanged between the client and server within an HTTP/2 connection. –rfc7540 StreamsLayer

“流”是一個邏輯上的概念(沒有真正傳輸流這麼個東西),是 HTTP2 連接中在客戶端和服務器之間交換的獨立雙向幀序列,這就是爲什麼在規範中的 stream 也是用雙引號括起來的原因。從上一節我們可以知道,HTTP2 的傳輸單位是,流其實就是一個幀的分組集合的概念,爲什麼需要這個邏輯集合呢?答案就在多路複用

多路複用是解決 HTTP1.x 缺陷第一點(併發問題)和第二點(HOLB線頭問題)的核心技術點。這裏需要舉個��來說明:

假設已經建立了 TCP 連接,現在需要客戶端發起了兩個請求,從流的角度看是這樣的:

stream-request

但是實際 TCP 連接只有一個,兩個幀是不可能真的“同時”到達服務器的,多路複用更像是 CPU 處理任務概念中的 併發,而不是並行,從規範中使用術語 Stream Concurrency 而不是 Stream Parallelism 也可得出此結論,所以實際傳輸時是下圖:

real-transport

上圖需要注意三點:

  1. 同一個流中的幀是交錯傳輸的!
  2. Header 幀必須在 data 幀前面,因爲無論是客戶端還是服務端,都依賴 header 幀的信息解析 data 幀的數據!
  3. 先到的幀不一定先返回,快的可以先返回!

正是由於上述第一點特性,解釋了爲什麼需要“流”這個邏輯集合。同時,通過這種 幀-流-連接 的組合,解決了請求併發(一次連接多個請求)和HOLB線頭問題(併發發送,異步響應)。

進入 HTTP/2: the Future of the Internet 由 Akamai 公司建立的官方 Demo,可以看出 HTTP2 相比於之前的 HTTP1.1 在性能上的大幅度提升。

HTTP1.1
http1

HTTP2
http2

在過去,我們發現 HTTP 性能優化的關鍵不在於高寬帶,而是低延遲

latency-vs-bandwidth

從上圖可見,當帶寬到達一定的速度之後,對頁面加載速度的提升已經很少了,但是隨着延遲的減少,頁面加載時間會對應持續的減少。由於 TCP 連接存在一種稱爲「調諧」的慢啓動(slow start)特性,讓原本就具有突發性和短時性的 HTTP 連接變的十分低效。而 HTTP2 通過讓所有數據流共用同一個連接,可以更有效地使用 TCP 連接,讓高帶寬也能真正的服務於 HTTP 的性能提升。

以上純屬個人理解,如有不對請不吝指出共同討論,感謝!

2.4 頭壓縮

我們都知道 HTTP協議本身是無狀態(stateless) 的:每個請求之間互不關聯,每個請求都需要攜帶服務器所需要的所有細節信息。比如說請求1發送給服務器信息“我是用戶A”,然後請求二發送信息“修改我的用戶名爲XX”,這時如果請求二沒有攜帶“我是用戶A”的信息,那麼服務器是不知道要修改哪個用戶的用戶名的。

這顯然是不符合當前 web 應用系統架構的,因爲一般系統都需要進行鑑權,日誌記錄,安全校驗等限制,所以需要獲取當前操作用戶的信息,出於安全和性能考慮我們不能在消息體中明文包含這些信息,HTTP2 之前的解決方案一般是使用 Cookies 頭、服務器session 等方式模擬出“狀態”。而使用 Cookies 頭的缺點就是每個請求都需要攜帶龐大的重複的信息並且無法壓縮,假設一個請求的 header 是2kb,那麼一百個請求就是重複的 200Kb 信息,這是一個巨大的帶寬浪費。

HTTP2 增加了兩個特性解決上述問題:

  • HPACK,專門爲頭部壓縮設計的算法,還被指定成單獨的草案中。
    hpack
  • 首部表,HTTP2 在戶端和服務器端使用“首部表”來跟蹤和存儲之前發送的鍵-值對,對於相同的數據,不再通過每次請求和響應發送;通信期間幾乎不會改變的通用鍵-值對(用戶代理、可接受的媒體類型,等等)只需發送一次。
    header-table

2.5 服務器推送

這個功能通常被稱作“緩存推送(cache push)”。主要的思想是:當一個客戶端請求資源X,而服務器知道它很可能也需要資源Z的情況下,服務器可以在客戶端發送請求Z前,主動將資源Z推送給客戶端。這個功能幫助客戶端將Z放進緩存以備將來之需。

服務器推送需要客戶端顯式的允許服務器提供該功能。但即使如此,客戶端依然能自主選擇是否需要中斷該推送的流。如果不需要的話,客戶端可以通過發送一個 RST_STREAM 幀來中止推送。

我們來看一下實際場景:現在我們訪問一個網站,第一個請求一般是獲取 Document 頁面,然後瀏覽器解析這個頁面,在遇到需要資源獲取的時候(css、js、圖片等),再去發起資源獲取請求,如下圖:

【傳統做法】
tradition

爲了加速這個過程,減少白屏時間,傳統的做法是把首頁需要的資源都內聯到 Document 中,還有合併資源比如 CSS sprites,js 壓縮合並等。如下圖:

inline

【HTTP2】

在 HTTP2 的場景下,客戶端在請求 Document 的時候,服務器如果知道頁面需要的資源有哪些,就可以把那些資源也一同返回了:

push

注意:主動推送的資源是能被瀏覽器緩存的!

那麼問題來了,如果客戶端已經緩存了資源,此時服務器每次都還推送同樣的資源給客戶端,這不是很大的浪費嗎?

答:原來確實會存在這種情況,所以 IETF 小組正在擬定一個名爲 cache-digest的技術規範,用於幫助客戶端主動告訴服務端哪些資源已經緩存了,不需要重複發送。

關於服務端推送對網頁性能的影響,和對於 CDN 的使用的比較,可以參考下面兩篇文章:

  1. measuring-server-push-performance
  2. http://www.ruanyifeng.com/blog/2018/03/http2_server_push.html

結論:使用 HTTP2 的多路複用和服務器推送功能,並不意味着可以減少甚至拋棄使用 CDN,因爲 CDN 帶來的現實地理位置上延遲減少是 HTTP2 所不能解決的,反而我們應該思考的是如何把 HTTP2 和 CDN 結合起來,進一步提升網絡服務的效率和穩定性,減少延遲。

2.6 優先級與依賴性

每個流都包含一個優先級(也就是“權重”),它被用來告訴對端哪個流更重要。當資源有限的時候,服務器會根據優先級來選擇應該先發送哪些流。

藉助於PRIORITY幀,客戶端同樣可以告知服務器當前的流依賴於其他哪個流。該功能讓客戶端能建立一個優先級“樹”,所有“子流”會依賴於“父流”的傳輸完成情況。

優先級和依賴關係可以在傳輸過程中被動態的改變。這樣當用戶滾動一個全是圖片的頁面的時候,瀏覽器就能夠指定哪個圖片擁有更高的優先級。或者是在你切換標籤頁的時候,瀏覽器可以提升新切換到頁面所包含流的優先級。

2.7 可重置

HTTP1.x 的有一個缺點是:當一個含有確切值的 Content-Length 的 HTTP 消息被送出之後,你就很難中斷它了。當然,通常你可以斷開整個 TCP 鏈接(但也不總是可以這樣),但這樣導致的代價就是需要通過三次握手來重新建立一個新的TCP連接。

一個更好的方案是隻終止當前傳輸的消息並重新發送一個新的。在http2裏面,我們可以通過發送 RST_STREAM 幀來實現這種需求,從而避免浪費帶寬和中斷已有的連接。

2.8 流量控制

每個http2流都擁有自己的公示的流量窗口,它可以限制另一端發送數據。如果你正好知道SSH的工作原理的話,這兩者非常相似。

對於每個流來說,兩端都必須告訴對方自己還有足夠的空間來處理新的數據,而在該窗口被擴大前,另一端只被允許發送這麼多數據。

只有數據幀會受到流量控制。

總結

我們來歸納一下使用 HTTP2 能帶來的好處:

  • 更小的傳輸體積,更小或者省略重複的頭消息
  • 突破原有的 TCP 連接併發限制,使用一個 TCP 連接即可實現多請求併發,單鏈接也能減輕服務端的壓力(更少的內存和 CPU 使用)
  • 解決 HOLB 線頭問題,慢的請求或者先發送的請求不會阻塞其他請求的返回
  • 結合 CDN 提供實時性更高,延遲更低的內容分發代理服務,大大減少白屏時間
  • 數據傳輸優先級可控,使網站可以實現更靈活和強大的頁面控制
  • 能在不中斷 TCP 連接的情況下停止(重置)數據的發送

本文主要是學習筆記和個人理解,首發於 xlaoyu.info, 部分圖片和文字引用網絡,侵刪。

參考文章:

  1. Ideal HTTP Performance 作者是 Mark Nottingham,IETF HTTP Working Group 的主席,Akamai 公司的首席架構師。
  2. TCP那些事
  3. HTTP2概述
  4. HTTP2講解
  5. HTTP/2 is here, let’s optimize! Ilya Grigorik, Velocity SC 2015
  6. rfc7540
  7. HTTP2的總結
  8. HTTP2.0 的奇妙日常 – alloyteam
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章