国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項超值服

開通VIP
xxxxHub 都用上了 HTTP/2 ,它牛逼在哪?


現(xiàn)在很多站點(diǎn)都已經(jīng)棄掉 HTTP/1.1,轉(zhuǎn)而使用 HTTP/2 協(xié)議了,比如某Hub、B站、愛奇藝、騰訊視頻、淘寶等等。

那 HTTP/2 牛逼在哪?

不多 BB 了,直接發(fā)車!


HTTP/1.1 協(xié)議的性能問題

我們得先要了解下 HTTP/1.1 協(xié)議存在的性能問題,因為 HTTP/2 協(xié)議就是把這些性能問題逐個攻破了。

現(xiàn)在的站點(diǎn)相比以前變化太多了,比如:

  • 消息的大小變大了,從幾 KB 大小的消息,到幾 MB 大小的消息;

  • 頁面資源變多了,從每個頁面不到 10 個的資源,到每頁超 100 多個資源;

  • 內(nèi)容形式變多樣了,從單純到文本內(nèi)容,到圖片、視頻、音頻等內(nèi)容;

  • 實時性要求變高了,對頁面的實時性要求的應(yīng)用越來越多;

這些變化帶來的最大性能問題就是 HTTP/1.1  的高延遲,延遲高必然影響的就是用戶體驗。主要原因如下幾個:

  • 延遲難以下降,雖然現(xiàn)在網(wǎng)絡(luò)的「帶寬」相比以前變多了,但是延遲降到一定幅度后,就很難再下降了,說白了就是到達(dá)了延遲的下限;

  • 并發(fā)連接有限,谷歌瀏覽器最大并發(fā)連接數(shù)是 6 個,而且每一個連接都要經(jīng)過 TCP 和 TLS 握手耗時,以及 TCP 慢啟動過程給流量帶來的影響;

  • 隊頭阻塞問題,同一連接只能在完成一個 HTTP 事務(wù)(請求和響應(yīng))后,才能處理下一個事務(wù);

  • HTTP 頭部巨大且重復(fù),由于 HTTP 協(xié)議是無狀態(tài)的,每一個請求都得攜帶 HTTP 頭部,特別是對于有攜帶 cookie 的頭部,而 cookie 的大小通常很大;

  • 不支持服務(wù)器推送消息,因此當(dāng)客戶端需要獲取通知時,只能通過定時器不斷地拉取消息,這無疑浪費(fèi)大量了帶寬和服務(wù)器資源。

為了解決 HTTP/1.1 性能問題,具體的優(yōu)化手段你可以看這篇文章「我的 HTTP/1.1 好慢啊!」,這里我舉例幾個常見的優(yōu)化手段:

  • 將多張小圖合并成一張大圖供瀏覽器 JavaScript 來切割使用,這樣可以將多個請求合并成一個請求,但是帶來了新的問題,當(dāng)某張小圖片更新了,那么需要重新請求大圖片,浪費(fèi)了大量的網(wǎng)絡(luò)帶寬;

  • 將圖片的二進(jìn)制數(shù)據(jù)通過 base64 編碼后,把編碼數(shù)據(jù)嵌入到 HTML 或  CSS 文件中,以此來減少網(wǎng)絡(luò)請求次數(shù);

  • 將多個體積較小的 JavaScript 文件使用 webpack 等工具打包成一個體積更大的 JavaScript 文件,以一個請求替代了很多個請求,但是帶來的問題,當(dāng)某個 js 文件變化了,需要重新請求同一個包里的所有 js 文件;

  • 將同一個頁面的資源分散到不同域名,提升并發(fā)連接上限,因為瀏覽器通常對同一域名的 HTTP 連接最大只能是 6 個;

盡管對 HTTP/1.1 協(xié)議的優(yōu)化手段如此之多,但是效果還是不盡人意,因為這些手段都是對 HTTP/1.1  協(xié)議的“外部”做優(yōu)化,而一些關(guān)鍵的地方是沒辦法優(yōu)化的,比如請求-響應(yīng)模型、頭部巨大且重復(fù)、并發(fā)連接耗時、服務(wù)器不能主動推送等,要改變這些必須重新設(shè)計 HTTP 協(xié)議,于是 HTTP/2 就出來了!


兼容 HTTP/1.1

HTTP/2 出來的目的是為了改善 HTTP 的性能。協(xié)議升級有一個很重要的地方,就是要兼容老版本的協(xié)議,否則新協(xié)議推廣起來就相當(dāng)困難,所幸 HTTP/2 做到了兼容 HTTP/1.1 。

那么,HTTP/2 是怎么做的呢?

第一點(diǎn),HTTP/2 沒有在 URI 里引入新的協(xié)議名,仍然用「http://」表示明文協(xié)議,用「https://」表示加密協(xié)議,于是只需要瀏覽器和服務(wù)器在背后自動升級協(xié)議,這樣可以讓用戶意識不到協(xié)議的升級,很好的實現(xiàn)了協(xié)議的平滑升級。

第二點(diǎn),只在應(yīng)用層做了改變,還是基于 TCP 協(xié)議傳輸,應(yīng)用層方面為了保持功能上的兼容,HTTP/2 把 HTTP 分解成了「語義」和「語法」兩個部分,「語義」層不做改動,與 HTTP/1.1 完全一致,比如請求方法、狀態(tài)碼、頭字段等規(guī)則保留不變。

但是,HTTP/2 在「語法」層面做了很多改造,基本改變了 HTTP 報文的傳輸格式。


頭部壓縮

HTTP 協(xié)議的報文是由「Header + Body」構(gòu)成的,對于 Body  部分,HTTP/1.1 協(xié)議可以使用頭字段 「Content-Encoding」指定 Body 的壓縮方式,比如用 gzip 壓縮,這樣可以節(jié)約帶寬,但報文中的另外一部分 Header,是沒有針對它的優(yōu)化手段。

HTTP/1.1 報文中 Header 部分存在的問題:

  • 含很多固定的字段,比如Cookie、User Agent、Accept 等,這些字段加起來也高達(dá)幾百字節(jié)甚至上千字節(jié),所以有必要壓縮;

  • 大量的請求和響應(yīng)的報文里有很多字段值都是重復(fù)的,這樣會使得大量帶寬被這些冗余的數(shù)據(jù)占用了,所以有必須要避免重復(fù)性

  • 字段是 ASCII 編碼的,雖然易于人類觀察,但效率低,所以有必要改成二進(jìn)制編碼;

HTTP/2 對 Header 部分做了大改造,把以上的問題都解決了。

HTTP/2 沒使用常見的 gzip 壓縮方式來壓縮頭部,而是開發(fā)了 HPACK 算法,HPACK 算法主要包含三個組成部分:

  • 靜態(tài)字典;

  • 動態(tài)字典;

  • Huffman 編碼(壓縮算法);

客戶端和服務(wù)器兩端都會建立和維護(hù)「字典」,用長度較小的索引號表示重復(fù)的字符串,再用 Huffman 編碼壓縮數(shù)據(jù),可達(dá)到 50%~90% 的高壓縮率。

靜態(tài)表編碼

HTTP/2 為高頻出現(xiàn)在頭部的字符串和字段建立了一張靜態(tài)表,它是寫入到 HTTP/2 客戶端與服務(wù)器的代碼中的,不會變化的,靜態(tài)表里共有 61 組,如下圖:

表中的 Index 表示索引(Key),Header Value 表示索引對應(yīng)的  Value,Header Name 表示字段的名字,比如 Index 為 2 代表 GET,Index 為 8 代表狀態(tài)碼 200。

你可能注意到,表中有的 Index 沒有對應(yīng)的 Header Value,這是因為這些 Value 并不是固定的而是變化的,這些 Value 都會經(jīng)過 Huffman 編碼后,才會發(fā)送出去。

這么說有點(diǎn)抽象,我們來看個具體的例子,下面這個 server 頭部字段,在 HTTP/1.1 的形式如下:

server: nghttpx\r\n

算上冒號空格和末尾的\r\n,共占用了 17 字節(jié),而使用了靜態(tài)表和 Huffman 編碼,可以將它壓縮成 8 字節(jié),壓縮率大概 47 %

我抓了個 HTTP/2 協(xié)議的網(wǎng)絡(luò)包,你可以從下圖看到,高亮部分就是 server 頭部字段,只用了 8 個字節(jié)來表示 server 頭部數(shù)據(jù)。

根據(jù) RFC7541 規(guī)范,如果頭部字段屬于靜態(tài)表范圍,并且 Value 是變化,那么它的 HTTP/2 頭部前 2 位固定為 01,所以整個頭部格式如下圖:

HTTP/2 頭部由于基于二進(jìn)制編碼,就不需要冒號空格和末尾的\r\n作為分隔符,于是改用表示字符串長度(Value Length)來分割 Index 和 Value。

接下來,根據(jù)這個頭部格式來分析上面抓包的 server 頭部的二進(jìn)制數(shù)據(jù)。

首先,從靜態(tài)表中能查到 server 頭部字段的 Index 為 54,二進(jìn)制為 110110,再加上固定 01,頭部格式第 1 個字節(jié)就是 01110110,這正是上面抓包標(biāo)注的紅色部分的二進(jìn)制數(shù)據(jù)。

然后,第二個字節(jié)的首個比特位表示 Value 是否經(jīng)過 Huffman 編碼,剩余的 7 位表示 Value 的長度,比如這次例子的第二個字節(jié)為 10000110,首位比特位為 1 就代表 Value 字符串是經(jīng)過 Huffman 編碼的,經(jīng)過 Huffman 編碼的 Value 長度為 6。

最后,字符串 nghttpx 經(jīng)過 Huffman 編碼后壓縮成了 6 個字節(jié),Huffman 編碼的原理是將高頻出現(xiàn)的信息用「較短」的編碼表示,從而縮減字符串長度。

于是,在統(tǒng)計大量的 HTTP 頭部后,HTTP/2 根據(jù)出現(xiàn)頻率將 ASCII 碼編碼為了 Huffman 編碼表,可以在 RFC7541 文檔找到這張靜態(tài) Huffman 表,我就不把表的全部內(nèi)容列出來了,我只列出字符串 nghttpx 中每個字符對應(yīng)的 Huffman 編碼,如下圖:

通過查表后,字符串 nghttpx 的 Huffman 編碼在下圖看到,共 6 個字節(jié),每一個字符的 Huffman 編碼,我用相同的顏色將他們對應(yīng)起來了,最后的 7 位是補(bǔ)位的。

最終,server 頭部的二進(jìn)制數(shù)據(jù)對應(yīng)的靜態(tài)頭部格式如下:

動態(tài)表編碼

靜態(tài)表只包含了 61 種高頻出現(xiàn)在頭部的字符串,不在靜態(tài)表范圍內(nèi)的頭部字符串就要自行構(gòu)建動態(tài)表,它的 Index 從 62 起步,會在編碼解碼的時候隨時更新。

比如,第一次發(fā)送時頭部中的「user-agent 」字段數(shù)據(jù)有上百個字節(jié),經(jīng)過 Huffman 編碼發(fā)送出去后,客戶端和服務(wù)器雙方都會更新自己的動態(tài)表,添加一個新的 Index 號 62。那么在下一次發(fā)送的時候,就不用重復(fù)發(fā)這個字段的數(shù)據(jù)了,只用發(fā) 1 個字節(jié)的 Index 號就好了,因為雙方都可以根據(jù)自己的動態(tài)表獲取到字段的數(shù)據(jù)

而且,隨著在同一 HTTP/2 連接上發(fā)送的報文越來越多,客戶端和服務(wù)器雙方的「字典」積累的越來越多,理論上最終每個頭部字段都會變成 1 個字節(jié)的 Index,這樣便避免了大量的冗余數(shù)據(jù)的傳輸,大大節(jié)約了帶寬。

理想很美好,現(xiàn)實很骨感。動態(tài)表越大,占用的內(nèi)存也就越大,如果占用了太多內(nèi)存,是會影響服務(wù)器性能的,因此 Web 服務(wù)器都會提供類似 http2_max_requests 的配置,用于限制一個連接上能夠傳輸?shù)恼埱髷?shù)量,避免動態(tài)表無限增大,請求數(shù)量到達(dá)上限后,就會關(guān)閉 HTTP/2 連接來釋放內(nèi)存。

綜上,HTTP/2 頭部的編碼通過「靜態(tài)表、動態(tài)表、Huffman 編碼」共同完成的。


二進(jìn)制幀

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)報文劃分成了兩個幀(Frame,圖中的 HEADERS(首部)和 DATA(消息負(fù)載) 是幀的類型,也就是說一條 HTTP 響應(yīng),劃分成了兩個幀來傳輸,并且采用二進(jìn)制來編碼。

HTTP/2 二進(jìn)制幀的結(jié)構(gòu)如下圖:

幀頭(Fream Header)很小,只有 9 個字節(jié),幀開頭的前 3 個字節(jié)表示幀數(shù)據(jù)(Fream Playload)的長度。

幀長度后面的一個字節(jié)是表示幀的類型,HTTP/2 總共定義了 10 種類型的幀,一般分為數(shù)據(jù)幀控制幀兩類,如下表格:

幀類型后面的一個字節(jié)是標(biāo)志位,可以保存 8 個標(biāo)志位,用于攜帶簡單的控制信息,比如:

  • END_HEADERS 表示頭數(shù)據(jù)結(jié)束標(biāo)志,相當(dāng)于 HTTP/1 里頭后的空行(“\r\n”);

  • END_STREAM 表示單方向數(shù)據(jù)發(fā)送結(jié)束,后續(xù)不會再有數(shù)據(jù)幀。

  • PRIORITY 表示流的優(yōu)先級;

幀頭的最后 4 個字節(jié)是流標(biāo)識符(Stream ID),但最高位被保留不用,只有 31 位可以使用,因此流標(biāo)識符的最大值是 2^31,大約是 21 億,它的作用是用來標(biāo)識該 Fream 屬于哪個 Stream,接收方可以根據(jù)這個信息從亂序的幀里找到相同 Stream ID 的幀,從而有序組裝信息。

最后面就是幀數(shù)據(jù)了,它存放的是通過 HPACK  算法壓縮過的 HTTP 頭部和包體。


并發(fā)傳輸

知道了 HTTP/2 的幀結(jié)構(gòu)后,我們再來看看它是如何實現(xiàn)并發(fā)傳輸的。

我們都知道 HTTP/1.1 的實現(xiàn)是基于請求-響應(yīng)模型的。同一個連接中,HTTP 完成一個事務(wù)(請求與響應(yīng)),才能處理下一個事務(wù),也就是說在發(fā)出請求等待響應(yīng)的過程中,是沒辦法做其他事情的,如果響應(yīng)遲遲不來,那么后續(xù)的請求是無法發(fā)送的,也造成了隊頭阻塞的問題。

而 HTTP/2 就很牛逼了,通過 Stream 這個設(shè)計,多個 Stream 復(fù)用一條 TCP 連接,達(dá)到并發(fā)的效果,解決了 HTTP/1.1 隊頭阻塞的問題,提高了 HTTP 傳輸?shù)耐掏铝俊?/p>

為了理解 HTTP/2 的并發(fā)是怎樣實現(xiàn)的,我們先來理解 HTTP/2 中的 Stream、Message、Frame 這 3 個概念。

你可以從上圖中看到:

  • 1 個 TCP 連接包含一個或者多個 Stream,Stream 是 HTTP/2 并發(fā)的關(guān)鍵技術(shù);

  • Stream 里可以包含 1 個或多個 Message,Message 對應(yīng) HTTP/1 中的請求或響應(yīng),由 HTTP 頭部和包體構(gòu)成;

  • Message 里包含一條或者多個 Frame,F(xiàn)rame 是 HTTP/2 最小單位,以二進(jìn)制壓縮格式存放 HTTP/1 中的內(nèi)容(頭部和包體);

因此,我們可以得出 2 個結(jié)論:HTTP 消息可以由多個 Frame 構(gòu)成,以及 1 個 Frame 可以由多個 TCP 報文構(gòu)成。

在 HTTP/2 連接上,不同 Stream 的幀是可以亂序發(fā)送的(因此可以并發(fā)不同的 Stream ),因為每個幀的頭部會攜帶 Stream ID 信息,所以接收端可以通過 Stream ID 有序組裝成 HTTP 消息,而同一 Stream 內(nèi)部的幀必須是嚴(yán)格有序的。

客戶端和服務(wù)器雙方都可以建立 Stream, Stream ID 也是有區(qū)別的,客戶端建立的 Stream 必須是奇數(shù)號,而服務(wù)器建立的 Stream 必須是偶數(shù)號。

同一個連接中的 Stream ID 是不能復(fù)用的,只能順序遞增,所以當(dāng) Stream ID 耗盡時,需要發(fā)一個控制幀 GOAWAY,用來關(guān)閉 TCP 連接。

在 Nginx 中,可以通過 http2_max_concurrent_streams 配置來設(shè)置 Stream 的上限,默認(rèn)是 128 個。

HTTP/2 通過 Stream 實現(xiàn)的并發(fā),比 HTTP/1.1 通過 TCP 連接實現(xiàn)并發(fā)要牛逼的多,因為當(dāng) HTTP/2 實現(xiàn) 100 個并發(fā) Stream 時,只需要建立一次 TCP 連接,而  HTTP/1.1 需要建立 100 個 TCP 連接,每個 TCP 連接都要經(jīng)過TCP 握手、慢啟動以及 TLS 握手過程,這些都是很耗時的。

HTTP/2 還可以對每個 Stream 設(shè)置不同優(yōu)先級,幀頭中的「標(biāo)志位」可以設(shè)置優(yōu)先級,比如客戶端訪問 HTML/CSS 和圖片資源時,希望服務(wù)器先傳遞 HTML/CSS,再傳圖片,那么就可以通過設(shè)置 Stream 的優(yōu)先級來實現(xiàn),以此提高用戶體驗。


服務(wù)器主動推送資源

HTTP/1.1 不支持服務(wù)器主動推送資源給客戶端,都是由客戶端向服務(wù)器發(fā)起請求后,才能獲取到服務(wù)器響應(yīng)的資源。

比如,客戶端通過  HTTP/1.1 請求從服務(wù)器那獲取到了 HTML 文件,而 HTML 可能還需要依賴 CSS 來渲染頁面,這時客戶端還要再發(fā)起獲取 CSS 文件的請求,需要兩次消息往返,如下圖左邊部分:

如上圖右邊部分,在 HTTP/2 中,客戶端在訪問 HTML 時,服務(wù)器可以直接主動推送 CSS 文件,減少了消息傳遞的次數(shù)。

在 Nginx 中,如果你希望客戶端訪問 /test.html 時,服務(wù)器直接推送 /test.css,那么可以這么配置:

location /test.html { 
  http2_push /test.css; 
}

那 HTTP/2 的推送是怎么實現(xiàn)的?

客戶端發(fā)起的請求,必須使用的是奇數(shù)號 Stream,服務(wù)器主動的推送,使用的是偶數(shù)號 Stream。服務(wù)器在推送資源時,會通過 PUSH_PROMISE 幀傳輸 HTTP 頭部,并通過幀中的 Promised Stream ID 字段告知客戶端,接下來會在哪個偶數(shù)號 Stream 中發(fā)送包體。

如上圖,在 Stream 1 中通知客戶端 CSS 資源即將到來,然后在 Stream 2 中發(fā)送 CSS 資源,注意 Stream 1 和 2 是可以并發(fā)的。


總結(jié)

HTTP/2 協(xié)議其實還有很多內(nèi)容,比如流控制、流狀態(tài)、依賴關(guān)系等等。

這次主要介紹了關(guān)于 HTTP/2 是如何提示性能的幾個方向,它相比 HTTP/1 大大提高了傳輸效率、吞吐能力。

第一點(diǎn),對于常見的 HTTP 頭部通過靜態(tài)表和 Huffman 編碼的方式,將體積壓縮了近一半,而且針對后續(xù)的請求頭部,還可以建立動態(tài)表,將體積壓縮近 90%,大大提高了編碼效率,同時節(jié)約了帶寬資源。

不過,動態(tài)表并非可以無限增大, 因為動態(tài)表是會占用內(nèi)存的,動態(tài)表越大,內(nèi)存也越大,容易影響服務(wù)器總體的并發(fā)能力,因此服務(wù)器需要限制 HTTP/2 連接時長或者請求次數(shù)。

第二點(diǎn),HTTP/2 實現(xiàn)了 Stream 并發(fā),多個 Stream 只需復(fù)用 1 個 TCP 連接,節(jié)約了 TCP 和 TLS 握手時間,以及減少了 TCP 慢啟動階段對流量的影響。不同的 Stream ID 才可以并發(fā),即時亂序發(fā)送幀也沒問題,但是同一個 Stream 里的幀必須嚴(yán)格有序。

另外,可以根據(jù)資源的渲染順序來設(shè)置 Stream 的優(yōu)先級,從而提高用戶體驗。

第三點(diǎn),服務(wù)器支持主動推送資源,大大提升了消息的傳輸性能,服務(wù)器推送資源時,會先發(fā)送 PUSH_PROMISE 幀,告訴客戶端接下來在哪個 Stream 發(fā)送資源,然后用偶數(shù)號 Stream 發(fā)送資源給客戶端。

HTTP/2 通過 Stream 的并發(fā)能力,解決了 HTTP/1 隊頭阻塞的問題,看似很完美了,但是 HTTP/2 還是存在“隊頭阻塞”的問題,只不過問題不是在 HTTP 這一層面,而是在 TCP 這一層。

HTTP/2 是基于 TCP 協(xié)議來傳輸數(shù)據(jù)的,TCP 是字節(jié)流協(xié)議,TCP 層必須保證收到的字節(jié)數(shù)據(jù)是完整且連續(xù)的,這樣內(nèi)核才會將緩沖區(qū)里的數(shù)據(jù)返回給 HTTP 應(yīng)用,那么當(dāng)「前 1 個字節(jié)數(shù)據(jù)」沒有到達(dá)時,后收到的字節(jié)數(shù)據(jù)只能存放在內(nèi)核緩沖區(qū)里,只有等到這 1 個字節(jié)數(shù)據(jù)到達(dá)時,HTTP/2 應(yīng)用層才能從內(nèi)核中拿到數(shù)據(jù),這就是 HTTP/2 隊頭阻塞問題。

有沒有什么解決方案呢?既然是 TCP 協(xié)議自身的問題,那干脆放棄 TCP 協(xié)議,轉(zhuǎn)而使用 UDP 協(xié)議作為傳輸層協(xié)議,這個大膽的決定, HTTP/3 協(xié)議做了!


巨人的肩膀
  1. https://developers.google.com/web/fundamentals/performance/http2

  2. https://http2.akamai.com/demo

  3. https://tools.ietf.org/html/rfc7541

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
為什么叫 HTTP/2 ,而不是 HTTP/2.0 ?
HTTP1.0 HTTP1.1 HTTP2.0 主要特性對比
搞懂HTTP只需要這51 張圖
一分鐘預(yù)覽 HTTP2 特性和抓包分析
Web端即時通訊技術(shù)盤點(diǎn):短輪詢、Comet、Websocket、SSE
理解TCP/IP協(xié)議棧之HTTP2.0
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服