前言
作為一隻前端開發?,HTTP是我們知識地圖裡面必不可少的一部分,也是面試必問知識點。HTTP2號稱可以讓我們的應用更快、更簡單、更穩定,它完美解決了1.1版本的諸多問題,本文和大家一起聊聊HTTP2的改進點。
HTTP發展史
正式講HTTP2之前我們先講一下HTTP的發展史。

- HTTP/0.9 - 單行協議
HTTP於1990年問世,那時候HTTP非常簡單:只支援GET方法;沒有首部;只能獲取純文字。 - HTTP/1.0 - 搭建協議的框架
1996年,HTTP正式被作為標準公佈,版本為HTTP/1.0。1.0版本增加了首部、狀態碼、許可權、快取、長連線(預設短連線)等規範,可以說搭建了協議的基本框架。 - HTTP/1.1 - 進一步完善
1997年,1.1版本接踵而至。1.1版本的重大改進在於預設長連線;強制客戶端提供Host首部;管線化;Cache-Control、ETag等快取的相關擴充套件。
目前存在的問題
現在我們先不聊HTTP2, 看一下HTTP發展到1.1存在有哪些問題:
- 線頭阻塞:TCP連線上只能傳送一個請求,前面的請求未完成前,後續的請求都在排隊等待。
- 多個TCP連線
雖然HTTP/1.1管線化可以支援請求併發,但是瀏覽器很難實現,chrome、firefox等都禁用了管線化。所以1.1版本請求併發依賴於多個TCP連線,建立TCP連線成本很高,還會存在慢啟動的問題。 - 頭部冗餘,採用文字格式
HTTP/1.X版本是採用文字格式,首部未壓縮,而且每一個請求都會帶上cookie、user-agent等完全相同的首部。 - 客戶端需要主動請求
HTTP/2.0的時代來了
先來一個demo感受一下吊炸天的HTTP/2.0,這個demo是載入379張圖片,來對比HTTP/1.1和HTTP/2.0的效能。 HTTP/1.1 與2.0 效能比較

理論上HTTP/2.0會比HTTP/1.1有一倍多的效能提升,弱網環境下,效能提升會更加明顯。 下面兩張圖是我在設定網路在fast 3G 和slow 3G的效能對比。


二進位制分幀層
HTTP2效能提升的核心就在於二進位制分幀層。HTTP2是二進位制協議,他採用二進位制格式傳輸資料而不是1.x的文字格式。

這裡我們來提三個概念。
- 流(Stream):已建立的TCP連線上的雙向位元組流,可以承載一個或多個訊息。
- 訊息(Message):一個完整的HTTP請求或響應,由一個或多個幀組成。特定訊息的幀在同一個流上傳送,這意味著一個HTTP請求或響應只能在一個流上傳送。
- 幀(Frame):通訊的基本單位。
一個TCP連線上可以有任意數量的流。
多路複用
上面提到HTTP/1.1的線頭阻塞和多個TCP連線的問題,HTTP2的多路複用完美解決。HTTP2讓所有的通訊都在一個TCP連線上完成,真正實現了請求的併發。我們來看一下HTTP2具體是怎麼實現的:


這裡例子我們能很直觀的看到就是多路複用起到的優化作用。因為HTTP2 實現了請求併發,後面的請求不用再等待,載入時長當然少了很多。截一張HTTP2的圖片載入耗時詳情來看看(要看比較靠後的請求):

頭部壓縮
頭部壓縮也是HTTP2的一大亮點。在1.X版本中,首部用文字格式傳輸,通常會給每個傳輸增加500-800位元組的開銷。現在開啟一個網頁上百個請求已是常態,而每個請求帶的一些首部欄位都是相同的,例如cookie、user-agent等。HTTP2為此採用HPACK壓縮格式來壓縮首部。頭部壓縮需要在瀏覽器和伺服器端之間:
- 維護一份相同的靜態字典,包含常見的頭部名稱,以及常見的頭部名稱和值的組合
- 維護一份相同的動態字典,可以動態的新增內容
- 通過靜態Huffman編碼對傳輸的首部欄位進行編碼
HTTP2的靜態字典是長這個樣子的(只擷取了部分,完整表格在這裡):

第一次傳輸過user-agent 之後呢,瀏覽器和伺服器端就會把它新增到自己的動態字典中。後續傳輸就可以傳輸索引了,一個位元組搞定。
我們用WireShark來抓包驗證一下:
HTTP2目前都是HTTPS的請求,WireShark對HTTPS網站抓包解密請參考這裡。
- 首次傳輸user-agent和第二次傳輸user-agent

- HPACK的首部壓縮力度


伺服器端推送
伺服器端推送使得伺服器可以預測客戶端需要的資源,主動推送到客戶端。
例如:客戶端請求index.html,伺服器端能夠額外推送script.js和style.css。
實現原理就是客戶端發出頁面請求時,伺服器端能夠分析這個頁面所依賴的其他資源,主動推送到客戶端的快取,當客戶端收到原始網頁的請求時,它需要的資源已經位於快取。
針對每一個希望傳送的資源,伺服器會傳送一個PUSH_PROMISE幀,客戶端可以通過傳送RST_STREAM幀來拒絕推送(當資源已經位於快取)。這一步的操作先於父響應(index.html),客戶端了解到伺服器端打算推送哪些資源,就不會再為這些資源建立重複請求。當客戶端收到index.html的響應時,script.js和style.css已經位於快取。
想要搭一個HTTP2伺服器的話推薦node,很簡單。連結
參考文章
結語
簡單講了HTTP2相比1.1版本的重要改進點,感受了一下h2的強大。還有一些流優先化等特性文中未涉及,感興趣的可以在參考文章中看看。如有錯誤,懇請指正!
@Author:小夭yao愛吃糖糖糖