大家好,我是前端西瓜哥。今天我們來聊聊 HTTP/2 的多路復用。
HTTP/1 下的請求,并不能很好地地利用帶寬:一個 TCP 連接同時只能有一個 HTTP 請求和響應。如果正在發送一個 HTTP 請求,那其他的 HTTP 請求就得排隊。
這種排隊會產生一個請求隊列,當隊頭的請求發生意外(比如丟包、服務器響應緩慢),導致比平時要慢得多,就會導致后面的請求被延遲。這種情況我們稱為 隊頭阻塞(Head-of-line blocking)。
為了緩解這個問題,瀏覽器會對同一個域名建立多個 TCP 連接,來實現 HTTP 的并發。
但這也對服務器造成不小的負擔,所以瀏覽器做了限制,同一個域名下 TCP 連接數最多會在 6 ~ 8 個左右。
如果網頁一次性加載的資源太多,比如大量圖片,6 個 TCP 連接數可能也會頂不住。為了解決一個問題,我們會使用 域名分片(Domain sharding) 的方法,就是將資源放到不同的域名下。
比如將圖片放到專門的 static.xxx.com ,或者 CDN。因為域名不同,所以總的 TCP 連接數就能突破 6 的限制。達到 域名數 x 6。
HTTP/1.1 有一個 pipeline 機制,意圖解決不能并發的問題,但因為實現上的缺陷,實質上已經廢棄。瀏覽器也默認關閉 pipeline。
為了解決這個問題,HTTP/2 使用了 多路復用。
HTTP/2 引入了流(stream)和幀(frame)的概念。
幀是最小的數據單位,HTTP 報文不再是原來的明文的 ASCII 編碼,而是會被拆分成一個個的二進制形式的幀。幀上面除了 HTTP 數據,還包含數據長度、流標識符、幀類型等信息。
流是一個建立連接后的雙向的虛擬字節流,可以承載多個消息。幀通過自己的流 ID,確定自己屬于哪個報文,就可以不按順序進行請求響應了。
HTTP/2 會將所有 HTTP 請求打散成幀,在一個 TCP 連接上做并發請求,充分利用 TCP 帶寬?,F在瀏覽器對于 HTTP2,只會建立一個 TCP 連接,減輕了服務端不小壓力。
例子
我們舉個例子講解 HTTP/1 升級為 HTTP/2 后利用多路復用帶來的優勢。
假設依次請求一個很大的 JS 文件,和一個很小的 css 文件。
在 HTTP/1 時,TCP 的發送的包是這樣的(JS 用多個 1 表示,CSS 用多個 2 表示):
111111111111111111111111222
JS 很大,會讓 CSS 延遲,我們可能希望比較小的 CSS 能早一點請求完,早一點做解析。而且 JS 一旦發生了意外發生阻塞,CSS 就更晚才能獲取到了。
現在我們用 HTTP/2,就變成了下面這樣:
121212111111111111111111111
因為并行的原因,CSS 不僅不用再擔心 JS 導致的阻塞,還能更早請求并獲取到資源。
結尾
HTTP/2 的多路復用能夠解決 HTTP 隊頭阻塞問題,更充分地利用 TCP 帶寬。
但因為還是在 TCP 上的協議,所以不能解決 TCP 隊頭阻塞問題,這個問題要交給 HTTP/3 通過 UDP 來解決了,期待一下。
我是前端西瓜哥,歡迎關注我,學習更多前端知識。