http2.0協(xié)議簡介
什么是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é)一下就是:
-
對1.x協(xié)議語意的完全兼容
2.0協(xié)議是在1.x基礎(chǔ)上的升級而不是重寫,1.x協(xié)議的方法暂氯,狀態(tài)及api在2.0協(xié)議里是一樣的。
-
性能的大幅提升
2.0協(xié)議重點是對終端用戶的感知延遲尔邓、網(wǎng)絡(luò)及服務(wù)器資源的使用等性能的優(yōu)化齿尽。
http2.0優(yōu)化內(nèi)容
二進制分幀(Binary Format)- http2.0的基石
http2.0之所以能夠突破http1.X標準的性能限制循头,改進傳輸性能炎疆,實現(xiàn)低延遲和高吞吐量卡骂,就是因為其新增了二進制分幀層。
幀(frame)包含部分:類型Type, 長度Length, 標記Flags, 流標識Stream和frame payload有效載荷形入。
消息(message):一個完整的請求或者響應(yīng)全跨,比如請求、響應(yīng)等亿遂,由一個或多個 Frame 組成浓若。
流是連接中的一個虛擬信道,可以承載雙向消息傳輸蛇数。每個流有唯一整數(shù)標識符。為了防止兩端流ID沖突,客戶端發(fā)起的流具有奇數(shù)ID蔓纠,服務(wù)器端發(fā)起的流具有偶數(shù)ID蚯妇。
流標識是描述二進制frame的格式硬贯,使得每個frame能夠基于http2發(fā)送务漩,與流標識聯(lián)系的是一個流,每個流是一個邏輯聯(lián)系,一個獨立的雙向的frame存在于客戶端和服務(wù)器端之間的http2連接中。一個http2連接上可包含多個并發(fā)打開的流祥楣,這個并發(fā)流的數(shù)量能夠由客戶端設(shè)置竭翠。
在二進制分幀層上,http2.0會將所有傳輸信息分割為更小的消息和幀,并對它們采用二進制格式的編碼將其封裝,新增的二進制分幀層同時也能夠保證http的各種動詞窥淆,方法筷畦,首部都不受影響,兼容上一代http標準帅刊。其中栏饮,http1.X中的首部信息header封裝到Headers幀中灶平,而request body將被封裝到Data幀中。
多路復(fù)用 (Multiplexing) / 連接共享
在http1.1中,瀏覽器客戶端在同一時間,針對同一域名下的請求有一定數(shù)量的限制。超過限制數(shù)目的請求會被阻塞。這也是為何一些站點會有多個靜態(tài)資源 CDN 域名的原因之一。而http2.0中的多路復(fù)用優(yōu)化了這一性能。多路復(fù)用允許同時通過單一的 http/2 連接發(fā)起多重的請求-響應(yīng)消息有勾。有了新的分幀機制后雇逞,http/2 不再依賴多個TCP連接去實現(xiàn)多流并行了晤锥。每個數(shù)據(jù)流都拆分成很多互不依賴的幀壕翩,而這些幀可以交錯(亂序發(fā)送)大猛,還可以分優(yōu)先級唉堪。最后再在另一端把它們重新組合起來灶搜。http 2.0 連接都是持久化的,而且客戶端與服務(wù)器之間也只需要一個連接(每個域名一個連接)即可丙挽。http2連接可以承載數(shù)十或數(shù)百個流的復(fù)用初婆,多路復(fù)用意味著來自很多流的數(shù)據(jù)包能夠混合在一起通過同樣連接傳輸。當?shù)竭_終點時敲董,再根據(jù)不同幀首部的流標識符重新連接將不同的數(shù)據(jù)流進行組裝萄窜。
上圖展示了一個連接上的多個傳輸數(shù)據(jù)流:客戶端向服務(wù)端傳輸數(shù)據(jù)幀stream5佃延,同時服務(wù)端向客戶端亂序發(fā)送stream1和stream3。這次連接上有三個響應(yīng)請求亂序并行交換。
上圖就是http1.X和http2.0在傳輸數(shù)據(jù)時的區(qū)別闷叉。以貨物運輸為例再現(xiàn)http1.1與http2.0的場景:
http1.1過程:貨輪1從A地到B地去取貨物萄传,取到貨物后赶么,從B地返回循捺,然后貨輪2在A返回并卸下貨物后才開始再從A地出發(fā)取貨返回叉谜,如此有序往返停局。
http2.0過程:貨輪1擒抛、2厢钧、3、4雀扶、5從A地無序全部出發(fā)浪册,取貨后返回,然后根據(jù)貨輪號牌卸載對應(yīng)貨物奇颠。
顯然吨铸,第二種方式運輸貨物多沼瘫,河道的利用率高歌焦。
頭部壓縮(Header Compression)
http1.x的頭帶有大量信息躁锁,而且每次都要重復(fù)發(fā)送。http/2使用encoder來減少需要傳輸?shù)膆eader大小膀懈,通訊雙方各自緩存一份頭部字段表硼控,既避免了重復(fù)header的傳輸纳决,又減小了需要傳輸?shù)拇笮∝仓τ谙嗤臄?shù)據(jù)担孔,不再通過每次請求和響應(yīng)發(fā)送;通信期間幾乎不會改變通用鍵-值對(用戶代理江锨、可接受的媒體類型,等等)只需發(fā)送一次。事實上,如果請求中不包含首部(例如對同一資源的輪詢請求),那么,首部開銷就是零字節(jié)糕篇。此時所有首部都自動使用之前請求發(fā)送的首部啄育。如果首部發(fā)生了變化,則只需將變化的部分加入到header幀中拌消,改變的部分會加入到頭部字段表中挑豌,首部表在 http 2.0 的連接存續(xù)期內(nèi)始終存在,由客戶端和服務(wù)器共同漸進地更新安券。需要注意的是,http 2.0關(guān)注的是首部壓縮氓英,而我們常用的gzip等是報文內(nèi)容(body)的壓縮侯勉。二者不僅不沖突,且能夠一起達到更好的壓縮效果铝阐。
http/2使用的是專門為首部壓縮而設(shè)計的HPACK算法址貌。
從上圖可以看到http1.X不支持首部壓縮,而http2.0的壓縮算法效果最好徘键,發(fā)送和接受的數(shù)據(jù)量都是最少的练对。
壓縮原理
用header字段表里的索引代替實際的header。
http/2 的 HPACK算法 使用一份索引表來定義常用的 http Header吹害,把常用的 http Header 存放在表里螟凭,請求的時候便只需要發(fā)送在表里的索引位置即可。例如 :method=GET 使用索引值 2 表示它呀,:path=/index.html 使用索引值 5 表示螺男,如下圖
完整的列表參考:HPACK Static Table。
只要給服務(wù)端發(fā)送一個 Frame纵穿,該 Frame 的 Payload 部分存儲 0x8285下隧,F(xiàn)rame 的 Type 設(shè)置為 Header 類型,便可表示這個 Frame 屬于 http Header政恍,請求的內(nèi)容是:
GET /index.html
為什么是 0x8285汪拥,而不是 0x0205? 這是因為高位設(shè)置為 1 表示這個字節(jié)是一個完全索引值(key 和 value 都在索引中)篙耗。類似的,通過高位的標志位可以區(qū)分出這個字節(jié)是屬于一個完全索引值宪赶,還是僅索引了 key宗弯,還是 key 和 value 都沒有索引(參見:HTTP/2首部壓縮的OkHttp3實現(xiàn))。因為索引表的大小的是有限的搂妻,它僅保存了一些常用的 http Header蒙保,同時每次請求還可以在表的末尾動態(tài)追加新的 http Header 緩存。動態(tài)部分稱之為 Dynamic Table欲主。Static Table 和 Dynamic Table 在一起組合成了索引表:
HPACK 不僅僅通過索引鍵值對來降低數(shù)據(jù)量邓厕,同時還會將字符串進行霍夫曼編碼來壓縮字符串大小。
以常用的 User-Agent 為例扁瓢,它在靜態(tài)表中的索引值是 58详恼,它的值是不存在表中的,因為它的值是多變的引几。第一次請求的時候它的 key 用 58 表示昧互,表示這是一個 User-Agent ,它的值部分會進行霍夫曼編碼(如果編碼后的字符串變更長了,則不采用霍夫曼編碼)敞掘。服務(wù)端收到請求后叽掘,會將這個 User-Agent 添加到 Dynamic Table 緩存起來,分配一個新的索引值玖雁「猓客戶端下一次請求時,假設(shè)上次請求User-Agent的在表中的索引位置是 62赫冬, 此時只需要發(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面殖。其過程如下圖所示:
最終竖哩,相同的 Header 只需要發(fā)送索引值,新的 Header 會重新加入 Dynamic Table脊僚。
請求優(yōu)先級(Request Priorities)
把http消息分為很多獨立幀之后相叁,就可以通過優(yōu)化這些幀的交錯和傳輸順序進一步優(yōu)化性能。每個流都可以帶有一個31比特的優(yōu)先值:0 表示最高優(yōu)先級辽幌;2的31次方-1 表示最低優(yōu)先級增淹。服務(wù)器可以根據(jù)流的優(yōu)先級,控制資源分配(CPU乌企、內(nèi)存虑润、帶寬),而在響應(yīng)數(shù)據(jù)準備好之后加酵,優(yōu)先將最高優(yōu)先級的幀發(fā)送給客戶端拳喻。高優(yōu)先級的流都應(yīng)該優(yōu)先發(fā)送,但又不會絕對的猪腕。絕對地準守冗澈,可能又會引入首隊阻塞的問題:高優(yōu)先級的請求慢導(dǎo)致阻塞其他資源交付。分配處理資源和客戶端與服務(wù)器間的帶寬陋葡,不同優(yōu)先級的混合也是必須的亚亲。客戶端會指定哪個流是最重要的腐缤,有一些依賴參數(shù)捌归,這樣一個流可以依賴另外一個流。優(yōu)先級別可以在運行時動態(tài)改變岭粤,當用戶滾動頁面時惜索,可以告訴瀏覽器哪個圖像是最重要的,你也可以在一組流中進行優(yōu)先篩選绍在,能夠突然抓住重點流门扇。
優(yōu)先級最高: 主要的html
優(yōu)先級高: CSS文件
優(yōu)先級中: js文件
優(yōu)先級低: 圖片
服務(wù)端推送(Server Push)
服務(wù)器可以對一個客戶端請求發(fā)送多個響應(yīng)雹有。服務(wù)器向客戶端推送資源無需客戶端明確地請求。服務(wù)端推送能把客戶端所需要的資源伴隨著index.html一起發(fā)送到客戶端臼寄,省去了客戶端重復(fù)請求的步驟霸奕。正因為沒有發(fā)起請求,建立連接等操作吉拳,所以靜態(tài)資源通過服務(wù)端推送的方式可以極大地提升速度质帅。Server Push 讓 http1.x 時代使用內(nèi)嵌資源的優(yōu)化手段變得沒有意義;如果一個請求是由你的主頁發(fā)起的留攒,服務(wù)器很可能會響應(yīng)主頁內(nèi)容煤惩、logo 以及樣式表,因為它知道客戶端會用到這些東西炼邀。這相當于在一個 HTML 文檔內(nèi)集合了所有的資源魄揉,不過與之相比,服務(wù)器推送還有一個很大的優(yōu)勢:可以緩存拭宁!也讓在遵循同源的情況下洛退,不同頁面之間可以共享緩存資源成為可能。
注意兩點:1.推送遵循同源策略杰标。2.這種服務(wù)端的推送是基于客戶端的請求響應(yīng)來確定的兵怯。
當服務(wù)端需要主動推送某個資源時,便會發(fā)送一個 Frame Type 為 PUSH_PROMISE 的 Frame腔剂,里面帶了 PUSH 需要新建的 Stream ID媒区。意思是告訴客戶端:接下來我要用這個 ID 向你發(fā)送東西,客戶端準備好接著掸犬⊥噤觯客戶端解析 Frame 時,發(fā)現(xiàn)它是一個 PUSH_PROMISE 類型登渣,便會準備接收服務(wù)端要推送的流噪服。
http2.0性能瓶頸
啟用http2.0后會給性能帶來很大的提升,但同時也會帶來新的性能瓶頸胜茧。因為現(xiàn)在所有的壓力集中在底層一個TCP連接之上,TCP很可能就是下一個性能瓶頸仇味,比如TCP分組的隊首阻塞問題呻顽,單個TCP packet丟失導(dǎo)致整個連接阻塞,無法逃避丹墨,此時所有消息都會受到影響廊遍。未來,服務(wù)器端針對http 2.0下的TCP配置優(yōu)化至關(guān)重要贩挣。
如何升級http2.0協(xié)議
nginx服務(wù)器升級http2.0協(xié)議需要滿足如下條件:
nginx版本高于1.9.5喉前;
--with-http_ssl_module 跟 --with-http_v2_module
--with-http_ssl_module模塊是因為http2.0協(xié)議是一種https協(xié)議没酣。
查看你的nginx配置
nginx -V
這個是已經(jīng)添加了對應(yīng)模塊。沒有這兩個模塊的需要手動編譯安裝卵迂。
找到nginx文件目錄
編譯安裝nginx文件
./configure --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-http_v2_module
然后執(zhí)行如下命令裕便,進行編譯安裝。
make
make install
更改nginx配置
安裝結(jié)束后將nginx.config文件中443端口添加http2见咒;
啟動nginx
最后一步偿衰,重啟nginx nginx restart
(注意不要直接nginx -s reload
)。這時候你的站點就升級為了http2.0協(xié)議了改览。
檢測
升級完成后下翎,怎么確定自己的站點是http2.0協(xié)議呢?一般有如下幾種方法:
- chrome devtool
打開chrome調(diào)試工具宝当,在network勾選protocol項视事,h2代表的是http2.0協(xié)議,可以看到筆者的網(wǎng)站已經(jīng)都升級好了庆揩。
-
網(wǎng)站
SSL lab 一個SSL服務(wù)器檢測的網(wǎng)站俐东,對網(wǎng)站進行安全評級,并將檢測結(jié)果自動生成一個詳細的評價報告
-
插件
http/2 and SPDY indicator 這是一款檢測http2.0和SPDY協(xié)議(Google開發(fā)的基于TCP的會話層協(xié)議)的插件