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