遲來的HTTP2簡明教程
The standardization effort was supported by Chrome, Opera, Firefox,[9] Internet Explorer 11, Safari, Amazon Silk, and Edge browsers.[10] Most major browsers had added HTTP/2 support by the end of 2015.[11]
According to W3Techs, as of November 2017, 20.5% of the top 10 million websites supported HTTP/2.
這是一段來自維基百科的關於HTTP2的說明,截止2015年底,主流瀏覽器都已經對HTTP2做了支持,根據2017年11月的W3Techs報告說明,全球有1/5的大型網站都已經使用了HTTP2了。作為碼農的你已經可以預料HTTP2的時代即將到來,對於HTTP2的技術細節你都準備好了么?
HTTP2的設計要點
- 高度兼容HTTP1.1
- 減少客戶端伺服器的交互延遲
- Header壓縮
- Server Push
- Pipelining & Mulplexing
我們平常聽到的GZIP壓縮僅僅是針對HTTP請求的Body部分進行的,這隻能算是半壓縮。對於很多API服務來說,返回的內容體其實並不大,這個時候請求頭就佔據了大部分流量。HTTP2將魔爪伸到了HTTP頭部,這回是徹底的對整個請求都進行壓縮了。
HTTP2的頭壓縮原理完全不同於HTTP1.1,它將常用的HEADER鍵值對映射到一個靜態表裡面的索引值,於是很多頭部的鍵值對使用一個位置索引來表示就可以了。這樣便大大節省了頭部消息的長度。對於那些不常用的自定義的頭部會使用一個動態表來維護,具體原理有一定複雜度,這裡就不再啰嗦了。
Server Push不同於Websocket,Server Push一般是指伺服器主動向客戶端推送數據,這是一種單向的主動推送,而WebSocket是雙向的,這兩種技術不是競爭關係。Server Push可以用在伺服器主動向客戶端推送靜態資源,比如瀏覽器請求index.html時,伺服器除了返回網頁內容外,還會將index.html頁面裡面的各種css和js一起推送到瀏覽器緩存起來,當瀏覽器分析了網頁內容發現靜態資源時,不需要再去伺服器請求一次,它只需要從緩存里直接拿就可以了。不過現代的網站的靜態資源大多都是CDN架構的,靜態資源都在第三方伺服器,Server Push在這方面作用並不大。Server Push還可以用在推送通知消息,比如誰關注了你,誰給你點了贊等,這個可以替代古老的Comet技術和近幾年Google推廣的SPDY協議,它需要伺服器維持當前的TCP通道不關閉,需要持續佔用伺服器資源。
Pipeline是指後一個HTTP請求無需等待前一個HTTP請求返回結果就可以提前發起。HTTP1.1也有Pipeline支持,但是有所不足,並行的還不夠徹底。它可以提前發起請求,但是卻限定了返回結果必須和收到請求的順序保持一致而不能亂序。如果第一個請求伺服器處理慢了,那麼後續的返回結果客戶端無法立即收到,而必須等到第一個結果全部返回了才行。HTTP2則解決了這個問題,它支持亂序返回,甚至不同請求的返回結果的分塊【HTTP Chunk】也可以交叉返回而不會混亂,這種技術稱之為Multiplexing【多路復用】。
HTTP2底層協議
HTTP2協議是二進位協議,不同於HTTP1.1的文本協議。文本協議是以特殊的符號結尾【換行回車符】來分割消息的,而二進位協議是通過位元組長度來分割消息。二進位協議雖然直觀性不如文本協議,但是在實現的時候要簡單直接一些。
HTTP2為支持多路復用,在同一條TCP通道上支持發送多個資源/請求,將每條資源/請求定義為一個Stream【流】,同一個TCP通道可以傳輸多個Stream。同時為了支持多個資源的並行交錯發送,將Stream再次分割為多個Frame【幀】,幀與幀之間可以交錯發送。接收端通過流ID將這些幀組裝起來,通一個流ID的幀屬於同一個資源/請求。因為TCP協議已經可以保證消息包是有序的,所以接收端不必擔心亂序問題。
HTTP2的幀格式非常簡單,就是長度+類型+標誌位+流ID+PayLoad,長度就是PayLoad的位元組數,類型為一個位元組,標誌位為1個位元組,流ID為4個位元組,剩下的長度就是PayLoad。不同類型的幀PayLoad不一樣,標誌位也不一樣。
HTTP2標準里定義了10種類型的幀。
- HEADERS幀 頭信息,對應於HTTP HEADER
- DATA幀 對應於HTTP Response Body
- PRIORITY幀 用於調整流的優先順序
- RST_STREAM幀 流終止幀,用於中斷資源的傳輸
- SETTINGS幀 用戶客戶伺服器交流連接配置信息
- PUSH_PROMISE幀 伺服器向客戶端主動推送資源
- GOAWAY幀 通知對方斷開連接
- PING幀 心跳幀,檢測往返時間和連接可用性
- WINDOW_UPDATE幀 調整幀大小
- CONTINUATION幀 HEADERS太大時的續幀
HTTP2標準定義了3個標誌位
- END_STREAM 流結束標誌,表示當前幀是流的最後一個幀
- END_HEADERS 頭結束表示,表示當前幀是頭信息的最後一個幀
- PADDED 填充標誌,在數據Payload里填充無用信息,用於干擾信道監聽
對於一個普通的GET請求來說,它使用一個HEADERS幀就可以表達。HEADERS幀會設置標誌位END_STREAM和END_HEADERS表示當前幀是一個完整的HEADERS幀,也是一個完整的HTTP請求流。
對於一個普通的GET請求響應來說,它使用一個HEADERS幀和多個DATA幀就可以表達。HEADERS幀設置END_HEADERS表示當前幀是一個完整的HEADERS幀,後面的DATA幀表示返回的數據,對應Response Body。在HTTP1.1裡面返回的Body長度較大,就需要分Chunk進行傳輸。HTTP2是通過分成多個DATA幀來進行的,最後一個DATA幀有一個END_STREAM標記表示Body的結束。
如果HEADERS太大無法用一個HEADERS幀表達,可以後面跟多個CONTINUATION幀,最後一個幀附加END_HEADERS標誌即可。
在伺服器主動向客戶端推送資源時,同一個資源流里不使用HEADERS幀,取而代之的是PUSH_PROMISE幀,表示伺服器承諾客戶端即將推送指定資源數據,用於區別一個常規的HTTP GET資源請求。
如果一個TCP連接正在被用於客戶端從伺服器下載一個大型文件,那麼客戶端取消發送這個文件的辦法只有一個,就是關閉連接。HTTP2則可以在不關閉連接的情況下終止發送文件,客戶端向伺服器發送一個RST_STREAM幀通知伺服器停止相應的資源流即可。這個連接還可以繼續服務其它的請求。
HTTP2伺服器接收到一個客戶端的連接時,第一個要乾的事就是和客戶端交換SETTINGS幀信息,告知對方一些交互元信息的設置,例如是否開啟伺服器推送,並行的最大流數量,單幀最大長度等。
在客戶端觀看視頻流時,如果伺服器發送的太慢會影響觀看體驗,如果發的太快,會對客戶端的瀏覽器緩存造成壓力。客戶端可以使用WINDOW_UPDATE幀通知伺服器調整幀窗口大小進而控制服務區發送的數據速率。
閱讀相關文章,關注知乎專欄【碼洞】
推薦閱讀: