深入理解http2.0協(xié)議,看這篇就夠了

http2.0協(xié)議簡(jiǎn)介

什么是http2.0協(xié)議?

http2.0官網(wǎng) 的描述是:


http/2 is a replacement for how http is expressed “on the wire.” It is not a ground-up rewrite of the protocol; http methods, status codes and semantics are the same, and it should be possible to use the same APIs as http/1.x (possibly with some small additions) to represent the protocol.

The focus of the protocol is on performance; specifically, end-user perceived latency, network and server resource usage. One major goal is to allow the use of a single connection from browsers to a Web site.

The basis of the work was SPDY, but http/2 has evolved to take the community’s input into account, incorporating several improvements in the process.

中文總結(jié)一下就是:

  • 對(duì)1.x協(xié)議語(yǔ)意的完全兼容

    2.0協(xié)議是在1.x基礎(chǔ)上的升級(jí)而不是重寫,1.x協(xié)議的方法,狀態(tài)及api在2.0協(xié)議里是一樣的。

  • 性能的大幅提升

    2.0協(xié)議重點(diǎn)是對(duì)終端用戶的感知延遲、網(wǎng)絡(luò)及服務(wù)器資源的使用等性能的優(yōu)化。

http2.0優(yōu)化內(nèi)容

二進(jìn)制分幀(Binary Format)- http2.0的基石

http2.0之所以能夠突破http1.X標(biāo)準(zhǔn)的性能限制,改進(jìn)傳輸性能,實(shí)現(xiàn)低延遲和高吞吐量,就是因?yàn)槠湫略隽硕M(jìn)制分幀層。


    幀(frame)包含部分:類型Type, 長(zhǎng)度Length, 標(biāo)記Flags, 流標(biāo)識(shí)Stream和frame payload有效載荷。

    消息(message):一個(gè)完整的請(qǐng)求或者響應(yīng),比如請(qǐng)求、響應(yīng)等,由一個(gè)或多個(gè) Frame 組成。

    流是連接中的一個(gè)虛擬信道,可以承載雙向消息傳輸。每個(gè)流有唯一整數(shù)標(biāo)識(shí)符。為了防止兩端流ID沖突,客戶端發(fā)起的流具有奇數(shù)ID,服務(wù)器端發(fā)起的流具有偶數(shù)ID。

    流標(biāo)識(shí)是描述二進(jìn)制frame的格式,使得每個(gè)frame能夠基于http2發(fā)送,與流標(biāo)識(shí)聯(lián)系的是一個(gè)流,每個(gè)流是一個(gè)邏輯聯(lián)系,一個(gè)獨(dú)立的雙向的frame存在于客戶端和服務(wù)器端之間的http2連接中。一個(gè)http2連接上可包含多個(gè)并發(fā)打開的流,這個(gè)并發(fā)流的數(shù)量能夠由客戶端設(shè)置。

在二進(jìn)制分幀層上,http2.0會(huì)將所有傳輸信息分割為更小的消息和幀,并對(duì)它們采用二進(jìn)制格式的編碼將其封裝,新增的二進(jìn)制分幀層同時(shí)也能夠保證http的各種動(dòng)詞,方法,首部都不受影響,兼容上一代http標(biāo)準(zhǔn)。其中,http1.X中的首部信息header封裝到Headers幀中,而request body將被封裝到Data幀中。

image

多路復(fù)用 (Multiplexing) / 連接共享

在http1.1中,瀏覽器客戶端在同一時(shí)間,針對(duì)同一域名下的請(qǐng)求有一定數(shù)量的限制。超過(guò)限制數(shù)目的請(qǐng)求會(huì)被阻塞。這也是為何一些站點(diǎn)會(huì)有多個(gè)靜態(tài)資源 CDN 域名的原因之一。而http2.0中的多路復(fù)用優(yōu)化了這一性能。多路復(fù)用允許同時(shí)通過(guò)單一的 http/2 連接發(fā)起多重的請(qǐng)求-響應(yīng)消息。有了新的分幀機(jī)制后,http/2 不再依賴多個(gè)TCP連接去實(shí)現(xiàn)多流并行了。每個(gè)數(shù)據(jù)流都拆分成很多互不依賴的幀,而這些幀可以交錯(cuò)(亂序發(fā)送),還可以分優(yōu)先級(jí)。最后再在另一端把它們重新組合起來(lái)。http 2.0 連接都是持久化的,而且客戶端與服務(wù)器之間也只需要一個(gè)連接(每個(gè)域名一個(gè)連接)即可。http2連接可以承載數(shù)十或數(shù)百個(gè)流的復(fù)用,多路復(fù)用意味著來(lái)自很多流的數(shù)據(jù)包能夠混合在一起通過(guò)同樣連接傳輸。當(dāng)?shù)竭_(dá)終點(diǎn)時(shí),再根據(jù)不同幀首部的流標(biāo)識(shí)符重新連接將不同的數(shù)據(jù)流進(jìn)行組裝。

image

上圖展示了一個(gè)連接上的多個(gè)傳輸數(shù)據(jù)流:客戶端向服務(wù)端傳輸數(shù)據(jù)幀stream5,同時(shí)服務(wù)端向客戶端亂序發(fā)送stream1和stream3。這次連接上有三個(gè)響應(yīng)請(qǐng)求亂序并行交換。

image

上圖就是http1.X和http2.0在傳輸數(shù)據(jù)時(shí)的區(qū)別。以貨物運(yùn)輸為例再現(xiàn)http1.1與http2.0的場(chǎng)景:


http1.1過(guò)程:貨輪1從A地到B地去取貨物,取到貨物后,從B地返回,然后貨輪2在A返回并卸下貨物后才開始再?gòu)腁地出發(fā)取貨返回,如此有序往返。

http2.0過(guò)程:貨輪1、2、3、4、5從A地?zé)o序全部出發(fā),取貨后返回,然后根據(jù)貨輪號(hào)牌卸載對(duì)應(yīng)貨物。

顯然,第二種方式運(yùn)輸貨物多,河道的利用率高。

頭部壓縮(Header Compression)

http1.x的頭帶有大量信息,而且每次都要重復(fù)發(fā)送。http/2使用encoder來(lái)減少需要傳輸?shù)膆eader大小,通訊雙方各自緩存一份頭部字段表,既避免了重復(fù)header的傳輸,又減小了需要傳輸?shù)拇笮?。?duì)于相同的數(shù)據(jù),不再通過(guò)每次請(qǐng)求和響應(yīng)發(fā)送;通信期間幾乎不會(huì)改變通用鍵-值對(duì)(用戶代理、可接受的媒體類型,等等)只需發(fā)送一次。事實(shí)上,如果請(qǐng)求中不包含首部(例如對(duì)同一資源的輪詢請(qǐng)求),那么,首部開銷就是零字節(jié)。此時(shí)所有首部都自動(dòng)使用之前請(qǐng)求發(fā)送的首部。如果首部發(fā)生了變化,則只需將變化的部分加入到header幀中,改變的部分會(huì)加入到頭部字段表中,首部表在 http 2.0 的連接存續(xù)期內(nèi)始終存在,由客戶端和服務(wù)器共同漸進(jìn)地更新。需要注意的是,http 2.0關(guān)注的是首部壓縮,而我們常用的gzip等是報(bào)文內(nèi)容(body)的壓縮。二者不僅不沖突,且能夠一起達(dá)到更好的壓縮效果。

http/2使用的是專門為首部壓縮而設(shè)計(jì)的HPACK算法。

image

從上圖可以看到http1.X不支持首部壓縮,而http2.0的壓縮算法效果最好,發(fā)送和接受的數(shù)據(jù)量都是最少的。

壓縮原理

用header字段表里的索引代替實(shí)際的header。

http/2 的 HPACK算法 使用一份索引表來(lái)定義常用的 http Header,把常用的 http Header 存放在表里,請(qǐng)求的時(shí)候便只需要發(fā)送在表里的索引位置即可。例如 :method=GET 使用索引值 2 表示,:path=/index.html 使用索引值 5 表示,如下圖

image

完整的列表參考:HPACK Static Table。

只要給服務(wù)端發(fā)送一個(gè) Frame,該 Frame 的 Payload 部分存儲(chǔ) 0x8285,F(xiàn)rame 的 Type 設(shè)置為 Header 類型,便可表示這個(gè) Frame 屬于 http Header,請(qǐng)求的內(nèi)容是:


GET /index.html

為什么是 0x8285,而不是 0x0205? 這是因?yàn)楦呶辉O(shè)置為 1 表示這個(gè)字節(jié)是一個(gè)完全索引值(key 和 value 都在索引中)。類似的,通過(guò)高位的標(biāo)志位可以區(qū)分出這個(gè)字節(jié)是屬于一個(gè)完全索引值,還是僅索引了 key,還是 key 和 value 都沒有索引(參見:HTTP/2首部壓縮的OkHttp3實(shí)現(xiàn))。因?yàn)樗饕淼拇笮〉氖怯邢薜?,它僅保存了一些常用的 http Header,同時(shí)每次請(qǐng)求還可以在表的末尾動(dòng)態(tài)追加新的 http Header 緩存。動(dòng)態(tài)部分稱之為 Dynamic Table。Static Table 和 Dynamic Table 在一起組合成了索引表:

image

HPACK 不僅僅通過(guò)索引鍵值對(duì)來(lái)降低數(shù)據(jù)量,同時(shí)還會(huì)將字符串進(jìn)行霍夫曼編碼來(lái)壓縮字符串大小。

以常用的 User-Agent 為例,它在靜態(tài)表中的索引值是 58,它的值是不存在表中的,因?yàn)樗闹凳嵌嘧兊?。第一次?qǐng)求的時(shí)候它的 key 用 58 表示,表示這是一個(gè) User-Agent ,它的值部分會(huì)進(jìn)行霍夫曼編碼(如果編碼后的字符串變更長(zhǎng)了,則不采用霍夫曼編碼)。服務(wù)端收到請(qǐng)求后,會(huì)將這個(gè) User-Agent 添加到 Dynamic Table 緩存起來(lái),分配一個(gè)新的索引值。客戶端下一次請(qǐng)求時(shí),假設(shè)上次請(qǐng)求User-Agent的在表中的索引位置是 62, 此時(shí)只需要發(fā)送 0xBE(同樣的,高位置 1),便可以代表: User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36。其過(guò)程如下圖所示:

image

最終,相同的 Header 只需要發(fā)送索引值,新的 Header 會(huì)重新加入 Dynamic Table。

請(qǐng)求優(yōu)先級(jí)(Request Priorities)

把http消息分為很多獨(dú)立幀之后,就可以通過(guò)優(yōu)化這些幀的交錯(cuò)和傳輸順序進(jìn)一步優(yōu)化性能。每個(gè)流都可以帶有一個(gè)31比特的優(yōu)先值:0 表示最高優(yōu)先級(jí);2的31次方-1 表示最低優(yōu)先級(jí)。服務(wù)器可以根據(jù)流的優(yōu)先級(jí),控制資源分配(CPU、內(nèi)存、帶寬),而在響應(yīng)數(shù)據(jù)準(zhǔn)備好之后,優(yōu)先將最高優(yōu)先級(jí)的幀發(fā)送給客戶端。高優(yōu)先級(jí)的流都應(yīng)該優(yōu)先發(fā)送,但又不會(huì)絕對(duì)的。絕對(duì)地準(zhǔn)守,可能又會(huì)引入首隊(duì)阻塞的問(wèn)題:高優(yōu)先級(jí)的請(qǐng)求慢導(dǎo)致阻塞其他資源交付。分配處理資源和客戶端與服務(wù)器間的帶寬,不同優(yōu)先級(jí)的混合也是必須的。客戶端會(huì)指定哪個(gè)流是最重要的,有一些依賴參數(shù),這樣一個(gè)流可以依賴另外一個(gè)流。優(yōu)先級(jí)別可以在運(yùn)行時(shí)動(dòng)態(tài)改變,當(dāng)用戶滾動(dòng)頁(yè)面時(shí),可以告訴瀏覽器哪個(gè)圖像是最重要的,你也可以在一組流中進(jìn)行優(yōu)先篩選,能夠突然抓住重點(diǎn)流。

  • 優(yōu)先級(jí)最高: 主要的html

  • 優(yōu)先級(jí)高: CSS文件

  • 優(yōu)先級(jí)中: js文件

  • 優(yōu)先級(jí)低: 圖片

服務(wù)端推送(Server Push)

服務(wù)器可以對(duì)一個(gè)客戶端請(qǐng)求發(fā)送多個(gè)響應(yīng)。服務(wù)器向客戶端推送資源無(wú)需客戶端明確地請(qǐng)求。服務(wù)端推送能把客戶端所需要的資源伴隨著index.html一起發(fā)送到客戶端,省去了客戶端重復(fù)請(qǐng)求的步驟。正因?yàn)闆]有發(fā)起請(qǐng)求,建立連接等操作,所以靜態(tài)資源通過(guò)服務(wù)端推送的方式可以極大地提升速度。Server Push 讓 http1.x 時(shí)代使用內(nèi)嵌資源的優(yōu)化手段變得沒有意義;如果一個(gè)請(qǐng)求是由你的主頁(yè)發(fā)起的,服務(wù)器很可能會(huì)響應(yīng)主頁(yè)內(nèi)容、logo 以及樣式表,因?yàn)樗揽蛻舳藭?huì)用到這些東西。這相當(dāng)于在一個(gè) HTML 文檔內(nèi)集合了所有的資源,不過(guò)與之相比,服務(wù)器推送還有一個(gè)很大的優(yōu)勢(shì):可以緩存!也讓在遵循同源的情況下,不同頁(yè)面之間可以共享緩存資源成為可能。

image

注意兩點(diǎn):1.推送遵循同源策略。2.這種服務(wù)端的推送是基于客戶端的請(qǐng)求響應(yīng)來(lái)確定的。

當(dāng)服務(wù)端需要主動(dòng)推送某個(gè)資源時(shí),便會(huì)發(fā)送一個(gè) Frame Type 為 PUSH_PROMISE 的 Frame,里面帶了 PUSH 需要新建的 Stream ID。意思是告訴客戶端:接下來(lái)我要用這個(gè) ID 向你發(fā)送東西,客戶端準(zhǔn)備好接著。客戶端解析 Frame 時(shí),發(fā)現(xiàn)它是一個(gè) PUSH_PROMISE 類型,便會(huì)準(zhǔn)備接收服務(wù)端要推送的流。

http2.0性能瓶頸

啟用http2.0后會(huì)給性能帶來(lái)很大的提升,但同時(shí)也會(huì)帶來(lái)新的性能瓶頸。因?yàn)楝F(xiàn)在所有的壓力集中在底層一個(gè)TCP連接之上,TCP很可能就是下一個(gè)性能瓶頸,比如TCP分組的隊(duì)首阻塞問(wèn)題,單個(gè)TCP packet丟失導(dǎo)致整個(gè)連接阻塞,無(wú)法逃避,此時(shí)所有消息都會(huì)受到影響。未來(lái),服務(wù)器端針對(duì)http 2.0下的TCP配置優(yōu)化至關(guān)重要。

如何升級(jí)http2.0協(xié)議

nginx服務(wù)器升級(jí)http2.0協(xié)議需要滿足如下條件:

  1. nginx版本高于1.9.5;

  2. --with-http_ssl_module 跟 --with-http_v2_module

--with-http_ssl_module模塊是因?yàn)閔ttp2.0協(xié)議是一種https協(xié)議。

查看你的nginx配置

nginx -V

image

這個(gè)是已經(jīng)添加了對(duì)應(yīng)模塊。沒有這兩個(gè)模塊的需要手動(dòng)編譯安裝。

找到nginx文件目錄

image

編譯安裝nginx文件


./configure --prefix=/usr/local/nginx  --with-http_stub_status_module  --with-http_ssl_module  --with-http_v2_module

image

然后執(zhí)行如下命令,進(jìn)行編譯安裝。


make

make install

image
image

更改nginx配置

安裝結(jié)束后將nginx.config文件中443端口添加http2;

image

啟動(dòng)nginx

最后一步,重啟nginx nginx restart(注意不要直接nginx -s reload )。這時(shí)候你的站點(diǎn)就升級(jí)為了http2.0協(xié)議了。

檢測(cè)

升級(jí)完成后,怎么確定自己的站點(diǎn)是http2.0協(xié)議呢?一般有如下幾種方法:

  • chrome devtool

打開chrome調(diào)試工具,在network勾選protocol項(xiàng),h2代表的是http2.0協(xié)議,可以看到筆者的網(wǎng)站已經(jīng)都升級(jí)好了。

image
  • 網(wǎng)站

    SSL lab 一個(gè)SSL服務(wù)器檢測(cè)的網(wǎng)站,對(duì)網(wǎng)站進(jìn)行安全評(píng)級(jí),并將檢測(cè)結(jié)果自動(dòng)生成一個(gè)詳細(xì)的評(píng)價(jià)報(bào)告

image
  • 插件

    http/2 and SPDY indicator 這是一款檢測(cè)http2.0和SPDY協(xié)議(Google開發(fā)的基于TCP的會(huì)話層協(xié)議)的插件

image
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容