現(xiàn)在很多站點(diǎn)都已經(jīng)棄掉 HTTP/1.1,轉(zhuǎn)而使用 HTTP/2 協(xié)議了,比如某Hub、B站、愛奇藝、騰訊視頻、淘寶等等。
那 HTTP/2 牛逼在哪?
不多 BB 了,直接發(fā)車!
我們得先要了解下 HTTP/1.1 協(xié)議存在的性能問題,因?yàn)?HTTP/2 協(xié)議就是把這些性能問題逐個(gè)攻破了。
現(xiàn)在的站點(diǎn)相比以前變化太多了,比如:
消息的大小變大了,從幾 KB 大小的消息,到幾 MB 大小的消息;
頁(yè)面資源變多了,從每個(gè)頁(yè)面不到 10 個(gè)的資源,到每頁(yè)超 100 多個(gè)資源;
內(nèi)容形式變多樣了,從單純到文本內(nèi)容,到圖片、視頻、音頻等內(nèi)容;
實(shí)時(shí)性要求變高了,對(duì)頁(yè)面的實(shí)時(shí)性要求的應(yīng)用越來越多;
這些變化帶來的最大性能問題就是 HTTP/1.1 的高延遲,延遲高必然影響的就是用戶體驗(yàn)。主要原因如下幾個(gè):
延遲難以下降,雖然現(xiàn)在網(wǎng)絡(luò)的「帶寬」相比以前變多了,但是延遲降到一定幅度后,就很難再下降了,說白了就是到達(dá)了延遲的下限;
并發(fā)連接有限,谷歌瀏覽器最大并發(fā)連接數(shù)是 6 個(gè),而且每一個(gè)連接都要經(jīng)過 TCP 和 TLS 握手耗時(shí),以及 TCP 慢啟動(dòng)過程給流量帶來的影響;
隊(duì)頭阻塞問題,同一連接只能在完成一個(gè) HTTP 事務(wù)(請(qǐng)求和響應(yīng))后,才能處理下一個(gè)事務(wù);
HTTP 頭部巨大且重復(fù),由于 HTTP 協(xié)議是無狀態(tài)的,每一個(gè)請(qǐng)求都得攜帶 HTTP 頭部,特別是對(duì)于有攜帶 cookie 的頭部,而 cookie 的大小通常很大;
不支持服務(wù)器推送消息,因此當(dāng)客戶端需要獲取通知時(shí),只能通過定時(shí)器不斷地拉取消息,這無疑浪費(fèi)大量了帶寬和服務(wù)器資源。
為了解決 HTTP/1.1 性能問題,具體的優(yōu)化手段你可以看這篇文章「我的 HTTP/1.1 好慢??!」,這里我舉例幾個(gè)常見的優(yōu)化手段:
將多張小圖合并成一張大圖供瀏覽器 JavaScript 來切割使用,這樣可以將多個(gè)請(qǐng)求合并成一個(gè)請(qǐng)求,但是帶來了新的問題,當(dāng)某張小圖片更新了,那么需要重新請(qǐng)求大圖片,浪費(fèi)了大量的網(wǎng)絡(luò)帶寬;
將圖片的二進(jìn)制數(shù)據(jù)通過 base64 編碼后,把編碼數(shù)據(jù)嵌入到 HTML 或 CSS 文件中,以此來減少網(wǎng)絡(luò)請(qǐng)求次數(shù);
將多個(gè)體積較小的 JavaScript 文件使用 webpack 等工具打包成一個(gè)體積更大的 JavaScript 文件,以一個(gè)請(qǐng)求替代了很多個(gè)請(qǐng)求,但是帶來的問題,當(dāng)某個(gè) js 文件變化了,需要重新請(qǐng)求同一個(gè)包里的所有 js 文件;
將同一個(gè)頁(yè)面的資源分散到不同域名,提升并發(fā)連接上限,因?yàn)闉g覽器通常對(duì)同一域名的 HTTP 連接最大只能是 6 個(gè);
盡管對(duì) HTTP/1.1 協(xié)議的優(yōu)化手段如此之多,但是效果還是不盡人意,因?yàn)檫@些手段都是對(duì) HTTP/1.1 協(xié)議的“外部”做優(yōu)化,而一些關(guān)鍵的地方是沒辦法優(yōu)化的,比如請(qǐng)求-響應(yīng)模型、頭部巨大且重復(fù)、并發(fā)連接耗時(shí)、服務(wù)器不能主動(dòng)推送等,要改變這些必須重新設(shè)計(jì) HTTP 協(xié)議,于是 HTTP/2 就出來了!
HTTP/2 出來的目的是為了改善 HTTP 的性能。協(xié)議升級(jí)有一個(gè)很重要的地方,就是要兼容老版本的協(xié)議,否則新協(xié)議推廣起來就相當(dāng)困難,所幸 HTTP/2 做到了兼容 HTTP/1.1 。
那么,HTTP/2 是怎么做的呢?
第一點(diǎn),HTTP/2 沒有在 URI 里引入新的協(xié)議名,仍然用「http://」表示明文協(xié)議,用「https://」表示加密協(xié)議,于是只需要瀏覽器和服務(wù)器在背后自動(dòng)升級(jí)協(xié)議,這樣可以讓用戶意識(shí)不到協(xié)議的升級(jí),很好的實(shí)現(xiàn)了協(xié)議的平滑升級(jí)。
第二點(diǎn),只在應(yīng)用層做了改變,還是基于 TCP 協(xié)議傳輸,應(yīng)用層方面為了保持功能上的兼容,HTTP/2 把 HTTP 分解成了「語義」和「語法」兩個(gè)部分,「語義」層不做改動(dòng),與 HTTP/1.1 完全一致,比如請(qǐng)求方法、狀態(tài)碼、頭字段等規(guī)則保留不變。
但是,HTTP/2 在「語法」層面做了很多改造,基本改變了 HTTP 報(bào)文的傳輸格式。
HTTP 協(xié)議的報(bào)文是由「Header + Body」構(gòu)成的,對(duì)于 Body 部分,HTTP/1.1 協(xié)議可以使用頭字段 「Content-Encoding」指定 Body 的壓縮方式,比如用 gzip 壓縮,這樣可以節(jié)約帶寬,但報(bào)文中的另外一部分 Header,是沒有針對(duì)它的優(yōu)化手段。
HTTP/1.1 報(bào)文中 Header 部分存在的問題:
含很多固定的字段,比如Cookie、User Agent、Accept 等,這些字段加起來也高達(dá)幾百字節(jié)甚至上千字節(jié),所以有必要壓縮;
大量的請(qǐng)求和響應(yīng)的報(bào)文里有很多字段值都是重復(fù)的,這樣會(huì)使得大量帶寬被這些冗余的數(shù)據(jù)占用了,所以有必須要避免重復(fù)性;
字段是 ASCII 編碼的,雖然易于人類觀察,但效率低,所以有必要改成二進(jìn)制編碼;
HTTP/2 對(duì) Header 部分做了大改造,把以上的問題都解決了。
HTTP/2 沒使用常見的 gzip 壓縮方式來壓縮頭部,而是開發(fā)了 HPACK 算法,HPACK 算法主要包含三個(gè)組成部分:
靜態(tài)字典;
動(dòng)態(tài)字典;
Huffman 編碼(壓縮算法);
客戶端和服務(wù)器兩端都會(huì)建立和維護(hù)「字典」,用長(zhǎng)度較小的索引號(hào)表示重復(fù)的字符串,再用 Huffman 編碼壓縮數(shù)據(jù),可達(dá)到 50%~90% 的高壓縮率。
HTTP/2 為高頻出現(xiàn)在頭部的字符串和字段建立了一張靜態(tài)表,它是寫入到 HTTP/2 客戶端與服務(wù)器的代碼中的,不會(huì)變化的,靜態(tài)表里共有 61
組,如下圖:
表中的 Index
表示索引(Key),Header Value
表示索引對(duì)應(yīng)的 Value,Header Name
表示字段的名字,比如 Index 為 2 代表 GET,Index 為 8 代表狀態(tài)碼 200。
你可能注意到,表中有的 Index 沒有對(duì)應(yīng)的 Header Value,這是因?yàn)檫@些 Value 并不是固定的而是變化的,這些 Value 都會(huì)經(jīng)過 Huffman 編碼后,才會(huì)發(fā)送出去。
這么說有點(diǎn)抽象,我們來看個(gè)具體的例子,下面這個(gè) server
頭部字段,在 HTTP/1.1 的形式如下:
server: nghttpx\r\n
算上冒號(hào)空格和末尾的\r\n,共占用了 17 字節(jié),而使用了靜態(tài)表和 Huffman 編碼,可以將它壓縮成 8 字節(jié),壓縮率大概 47 %。
我抓了個(gè) HTTP/2 協(xié)議的網(wǎng)絡(luò)包,你可以從下圖看到,高亮部分就是 server
頭部字段,只用了 8 個(gè)字節(jié)來表示 server
頭部數(shù)據(jù)。
根據(jù) RFC7541 規(guī)范,如果頭部字段屬于靜態(tài)表范圍,并且 Value 是變化,那么它的 HTTP/2 頭部前 2 位固定為 01
,所以整個(gè)頭部格式如下圖:
HTTP/2 頭部由于基于二進(jìn)制編碼,就不需要冒號(hào)空格和末尾的\r\n作為分隔符,于是改用表示字符串長(zhǎng)度(Value Length)來分割 Index 和 Value。
接下來,根據(jù)這個(gè)頭部格式來分析上面抓包的 server
頭部的二進(jìn)制數(shù)據(jù)。
首先,從靜態(tài)表中能查到 server
頭部字段的 Index 為 54,二進(jìn)制為 110110,再加上固定 01,頭部格式第 1 個(gè)字節(jié)就是 01110110
,這正是上面抓包標(biāo)注的紅色部分的二進(jìn)制數(shù)據(jù)。
然后,第二個(gè)字節(jié)的首個(gè)比特位表示 Value 是否經(jīng)過 Huffman 編碼,剩余的 7 位表示 Value 的長(zhǎng)度,比如這次例子的第二個(gè)字節(jié)為 10000110
,首位比特位為 1 就代表 Value 字符串是經(jīng)過 Huffman 編碼的,經(jīng)過 Huffman 編碼的 Value 長(zhǎng)度為 6。
最后,字符串 nghttpx
經(jīng)過 Huffman 編碼后壓縮成了 6 個(gè)字節(jié),Huffman 編碼的原理是將高頻出現(xiàn)的信息用「較短」的編碼表示,從而縮減字符串長(zhǎng)度。
于是,在統(tǒng)計(jì)大量的 HTTP 頭部后,HTTP/2 根據(jù)出現(xiàn)頻率將 ASCII 碼編碼為了 Huffman 編碼表,可以在 RFC7541 文檔找到這張靜態(tài) Huffman 表,我就不把表的全部?jī)?nèi)容列出來了,我只列出字符串 nghttpx
中每個(gè)字符對(duì)應(yīng)的 Huffman 編碼,如下圖:
通過查表后,字符串 nghttpx
的 Huffman 編碼在下圖看到,共 6 個(gè)字節(jié),每一個(gè)字符的 Huffman 編碼,我用相同的顏色將他們對(duì)應(yīng)起來了,最后的 7 位是補(bǔ)位的。
最終,server
頭部的二進(jìn)制數(shù)據(jù)對(duì)應(yīng)的靜態(tài)頭部格式如下:
靜態(tài)表只包含了 61 種高頻出現(xiàn)在頭部的字符串,不在靜態(tài)表范圍內(nèi)的頭部字符串就要自行構(gòu)建動(dòng)態(tài)表,它的 Index 從 62
起步,會(huì)在編碼解碼的時(shí)候隨時(shí)更新。
比如,第一次發(fā)送時(shí)頭部中的「user-agent
」字段數(shù)據(jù)有上百個(gè)字節(jié),經(jīng)過 Huffman 編碼發(fā)送出去后,客戶端和服務(wù)器雙方都會(huì)更新自己的動(dòng)態(tài)表,添加一個(gè)新的 Index 號(hào) 62。那么在下一次發(fā)送的時(shí)候,就不用重復(fù)發(fā)這個(gè)字段的數(shù)據(jù)了,只用發(fā) 1 個(gè)字節(jié)的 Index 號(hào)就好了,因?yàn)殡p方都可以根據(jù)自己的動(dòng)態(tài)表獲取到字段的數(shù)據(jù)。
而且,隨著在同一 HTTP/2 連接上發(fā)送的報(bào)文越來越多,客戶端和服務(wù)器雙方的「字典」積累的越來越多,理論上最終每個(gè)頭部字段都會(huì)變成 1 個(gè)字節(jié)的 Index,這樣便避免了大量的冗余數(shù)據(jù)的傳輸,大大節(jié)約了帶寬。
理想很美好,現(xiàn)實(shí)很骨感。動(dòng)態(tài)表越大,占用的內(nèi)存也就越大,如果占用了太多內(nèi)存,是會(huì)影響服務(wù)器性能的,因此 Web 服務(wù)器都會(huì)提供類似 http2_max_requests
的配置,用于限制一個(gè)連接上能夠傳輸?shù)恼?qǐng)求數(shù)量,避免動(dòng)態(tài)表無限增大,請(qǐng)求數(shù)量到達(dá)上限后,就會(huì)關(guān)閉 HTTP/2 連接來釋放內(nèi)存。
綜上,HTTP/2 頭部的編碼通過「靜態(tài)表、動(dòng)態(tài)表、Huffman 編碼」共同完成的。
HTTP/2 厲害的地方在于將 HTTP/1 的文本格式改成二進(jìn)制格式傳輸數(shù)據(jù),極大提高了 HTTP 傳輸效率,而且二進(jìn)制數(shù)據(jù)使用位運(yùn)算能高效解析。
你可以從下圖看到,HTTP/1.1 的響應(yīng) 和 HTTP/2 的區(qū)別:
HTTP/2 把響應(yīng)報(bào)文劃分成了兩個(gè)幀(Frame),圖中的 HEADERS(首部)和 DATA(消息負(fù)載) 是幀的類型,也就是說一條 HTTP 響應(yīng),劃分成了兩個(gè)幀來傳輸,并且采用二進(jìn)制來編碼。
HTTP/2 二進(jìn)制幀的結(jié)構(gòu)如下圖:
幀頭(Fream Header)很小,只有 9 個(gè)字節(jié),幀開頭的前 3 個(gè)字節(jié)表示幀數(shù)據(jù)(Fream Playload)的長(zhǎng)度。
幀長(zhǎng)度后面的一個(gè)字節(jié)是表示幀的類型,HTTP/2 總共定義了 10 種類型的幀,一般分為數(shù)據(jù)幀和控制幀兩類,如下表格:
幀類型后面的一個(gè)字節(jié)是標(biāo)志位,可以保存 8 個(gè)標(biāo)志位,用于攜帶簡(jiǎn)單的控制信息,比如:
END_HEADERS 表示頭數(shù)據(jù)結(jié)束標(biāo)志,相當(dāng)于 HTTP/1 里頭后的空行(“\r\n”);
END_STREAM 表示單方向數(shù)據(jù)發(fā)送結(jié)束,后續(xù)不會(huì)再有數(shù)據(jù)幀。
PRIORITY 表示流的優(yōu)先級(jí);
幀頭的最后 4 個(gè)字節(jié)是流標(biāo)識(shí)符(Stream ID),但最高位被保留不用,只有 31 位可以使用,因此流標(biāo)識(shí)符的最大值是 2^31,大約是 21 億,它的作用是用來標(biāo)識(shí)該 Fream 屬于哪個(gè) Stream,接收方可以根據(jù)這個(gè)信息從亂序的幀里找到相同 Stream ID 的幀,從而有序組裝信息。
最后面就是幀數(shù)據(jù)了,它存放的是通過 HPACK 算法壓縮過的 HTTP 頭部和包體。
知道了 HTTP/2 的幀結(jié)構(gòu)后,我們?cè)賮砜纯此侨绾螌?shí)現(xiàn)并發(fā)傳輸的。
我們都知道 HTTP/1.1 的實(shí)現(xiàn)是基于請(qǐng)求-響應(yīng)模型的。同一個(gè)連接中,HTTP 完成一個(gè)事務(wù)(請(qǐng)求與響應(yīng)),才能處理下一個(gè)事務(wù),也就是說在發(fā)出請(qǐng)求等待響應(yīng)的過程中,是沒辦法做其他事情的,如果響應(yīng)遲遲不來,那么后續(xù)的請(qǐng)求是無法發(fā)送的,也造成了隊(duì)頭阻塞的問題。
而 HTTP/2 就很牛逼了,通過 Stream 這個(gè)設(shè)計(jì),多個(gè) Stream 復(fù)用一條 TCP 連接,達(dá)到并發(fā)的效果,解決了 HTTP/1.1 隊(duì)頭阻塞的問題,提高了 HTTP 傳輸?shù)耐掏铝俊?/p>
為了理解 HTTP/2 的并發(fā)是怎樣實(shí)現(xiàn)的,我們先來理解 HTTP/2 中的 Stream、Message、Frame 這 3 個(gè)概念。
你可以從上圖中看到:
1 個(gè) TCP 連接包含一個(gè)或者多個(gè) Stream,Stream 是 HTTP/2 并發(fā)的關(guān)鍵技術(shù);
Stream 里可以包含 1 個(gè)或多個(gè) Message,Message 對(duì)應(yīng) HTTP/1 中的請(qǐng)求或響應(yīng),由 HTTP 頭部和包體構(gòu)成;
Message 里包含一條或者多個(gè) Frame,F(xiàn)rame 是 HTTP/2 最小單位,以二進(jìn)制壓縮格式存放 HTTP/1 中的內(nèi)容(頭部和包體);
因此,我們可以得出 2 個(gè)結(jié)論:HTTP 消息可以由多個(gè) Frame 構(gòu)成,以及 1 個(gè) Frame 可以由多個(gè) TCP 報(bào)文構(gòu)成。
在 HTTP/2 連接上,不同 Stream 的幀是可以亂序發(fā)送的(因此可以并發(fā)不同的 Stream ),因?yàn)槊總€(gè)幀的頭部會(huì)攜帶 Stream ID 信息,所以接收端可以通過 Stream ID 有序組裝成 HTTP 消息,而同一 Stream 內(nèi)部的幀必須是嚴(yán)格有序的。
客戶端和服務(wù)器雙方都可以建立 Stream, Stream ID 也是有區(qū)別的,客戶端建立的 Stream 必須是奇數(shù)號(hào),而服務(wù)器建立的 Stream 必須是偶數(shù)號(hào)。
同一個(gè)連接中的 Stream ID 是不能復(fù)用的,只能順序遞增,所以當(dāng) Stream ID 耗盡時(shí),需要發(fā)一個(gè)控制幀 GOAWAY
,用來關(guān)閉 TCP 連接。
在 Nginx 中,可以通過 http2_max_concurrent_streams
配置來設(shè)置 Stream 的上限,默認(rèn)是 128 個(gè)。
HTTP/2 通過 Stream 實(shí)現(xiàn)的并發(fā),比 HTTP/1.1 通過 TCP 連接實(shí)現(xiàn)并發(fā)要牛逼的多,因?yàn)楫?dāng) HTTP/2 實(shí)現(xiàn) 100 個(gè)并發(fā) Stream 時(shí),只需要建立一次 TCP 連接,而 HTTP/1.1 需要建立 100 個(gè) TCP 連接,每個(gè) TCP 連接都要經(jīng)過TCP 握手、慢啟動(dòng)以及 TLS 握手過程,這些都是很耗時(shí)的。
HTTP/2 還可以對(duì)每個(gè) Stream 設(shè)置不同優(yōu)先級(jí),幀頭中的「標(biāo)志位」可以設(shè)置優(yōu)先級(jí),比如客戶端訪問 HTML/CSS 和圖片資源時(shí),希望服務(wù)器先傳遞 HTML/CSS,再傳圖片,那么就可以通過設(shè)置 Stream 的優(yōu)先級(jí)來實(shí)現(xiàn),以此提高用戶體驗(yàn)。
HTTP/1.1 不支持服務(wù)器主動(dòng)推送資源給客戶端,都是由客戶端向服務(wù)器發(fā)起請(qǐng)求后,才能獲取到服務(wù)器響應(yīng)的資源。
比如,客戶端通過 HTTP/1.1 請(qǐng)求從服務(wù)器那獲取到了 HTML 文件,而 HTML 可能還需要依賴 CSS 來渲染頁(yè)面,這時(shí)客戶端還要再發(fā)起獲取 CSS 文件的請(qǐng)求,需要兩次消息往返,如下圖左邊部分:
如上圖右邊部分,在 HTTP/2 中,客戶端在訪問 HTML 時(shí),服務(wù)器可以直接主動(dòng)推送 CSS 文件,減少了消息傳遞的次數(shù)。
在 Nginx 中,如果你希望客戶端訪問 /test.html 時(shí),服務(wù)器直接推送 /test.css,那么可以這么配置:
location /test.html {
http2_push /test.css;
}
那 HTTP/2 的推送是怎么實(shí)現(xiàn)的?
客戶端發(fā)起的請(qǐng)求,必須使用的是奇數(shù)號(hào) Stream,服務(wù)器主動(dòng)的推送,使用的是偶數(shù)號(hào) Stream。服務(wù)器在推送資源時(shí),會(huì)通過 PUSH_PROMISE
幀傳輸 HTTP 頭部,并通過幀中的 Promised Stream ID
字段告知客戶端,接下來會(huì)在哪個(gè)偶數(shù)號(hào) Stream 中發(fā)送包體。
如上圖,在 Stream 1 中通知客戶端 CSS 資源即將到來,然后在 Stream 2 中發(fā)送 CSS 資源,注意 Stream 1 和 2 是可以并發(fā)的。
HTTP/2 協(xié)議其實(shí)還有很多內(nèi)容,比如流控制、流狀態(tài)、依賴關(guān)系等等。
這次主要介紹了關(guān)于 HTTP/2 是如何提示性能的幾個(gè)方向,它相比 HTTP/1 大大提高了傳輸效率、吞吐能力。
第一點(diǎn),對(duì)于常見的 HTTP 頭部通過靜態(tài)表和 Huffman 編碼的方式,將體積壓縮了近一半,而且針對(duì)后續(xù)的請(qǐng)求頭部,還可以建立動(dòng)態(tài)表,將體積壓縮近 90%,大大提高了編碼效率,同時(shí)節(jié)約了帶寬資源。
不過,動(dòng)態(tài)表并非可以無限增大, 因?yàn)閯?dòng)態(tài)表是會(huì)占用內(nèi)存的,動(dòng)態(tài)表越大,內(nèi)存也越大,容易影響服務(wù)器總體的并發(fā)能力,因此服務(wù)器需要限制 HTTP/2 連接時(shí)長(zhǎng)或者請(qǐng)求次數(shù)。
第二點(diǎn),HTTP/2 實(shí)現(xiàn)了 Stream 并發(fā),多個(gè) Stream 只需復(fù)用 1 個(gè) TCP 連接,節(jié)約了 TCP 和 TLS 握手時(shí)間,以及減少了 TCP 慢啟動(dòng)階段對(duì)流量的影響。不同的 Stream ID 才可以并發(fā),即時(shí)亂序發(fā)送幀也沒問題,但是同一個(gè) Stream 里的幀必須嚴(yán)格有序。
另外,可以根據(jù)資源的渲染順序來設(shè)置 Stream 的優(yōu)先級(jí),從而提高用戶體驗(yàn)。
第三點(diǎn),服務(wù)器支持主動(dòng)推送資源,大大提升了消息的傳輸性能,服務(wù)器推送資源時(shí),會(huì)先發(fā)送 PUSH_PROMISE 幀,告訴客戶端接下來在哪個(gè) Stream 發(fā)送資源,然后用偶數(shù)號(hào) Stream 發(fā)送資源給客戶端。
HTTP/2 通過 Stream 的并發(fā)能力,解決了 HTTP/1 隊(duì)頭阻塞的問題,看似很完美了,但是 HTTP/2 還是存在“隊(duì)頭阻塞”的問題,只不過問題不是在 HTTP 這一層面,而是在 TCP 這一層。
HTTP/2 是基于 TCP 協(xié)議來傳輸數(shù)據(jù)的,TCP 是字節(jié)流協(xié)議,TCP 層必須保證收到的字節(jié)數(shù)據(jù)是完整且連續(xù)的,這樣內(nèi)核才會(huì)將緩沖區(qū)里的數(shù)據(jù)返回給 HTTP 應(yīng)用,那么當(dāng)「前 1 個(gè)字節(jié)數(shù)據(jù)」沒有到達(dá)時(shí),后收到的字節(jié)數(shù)據(jù)只能存放在內(nèi)核緩沖區(qū)里,只有等到這 1 個(gè)字節(jié)數(shù)據(jù)到達(dá)時(shí),HTTP/2 應(yīng)用層才能從內(nèi)核中拿到數(shù)據(jù),這就是 HTTP/2 隊(duì)頭阻塞問題。
有沒有什么解決方案呢?既然是 TCP 協(xié)議自身的問題,那干脆放棄 TCP 協(xié)議,轉(zhuǎn)而使用 UDP 協(xié)議作為傳輸層協(xié)議,這個(gè)大膽的決定, HTTP/3 協(xié)議做了!
https://developers.google.com/web/fundamentals/performance/http2
https://http2.akamai.com/demo
https://tools.ietf.org/html/rfc7541
聯(lián)系客服