Http/2.0

導讀

http2.0是一種安全高效的下一代http傳輸協議。安全是因爲http2.0建立在https協議的基礎上,高效是因爲它是通過二進制分幀來進行數據傳輸。正因爲這些特性,http2.0協議也在被越來越多的網站支持。據統計,截止至2018年8月,已經有27.9%的網站支持http2.0。

本文將從概述、原理、實戰及檢測等方面來詳細介紹http2.0,希望能夠加深你的理解。

什麼是http2.0協議?

在http2.0官網①的描述是:

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.

中文總結一下就是:

●對1.x協議語意的完全兼容

2.0協議是在1.x基礎上的升級而不是重寫,1.x協議的方法,狀態及api在2.0協議裏是一樣的。

●性能的大幅提升

2.0協議重點是對終端用戶的感知延遲、網絡及服務器資源的使用等性能的優化。

http2.0優化內容

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

http2.0之所以能夠突破http1.X標準的性能限制,改進傳輸性能,實現低延遲和高吞吐量,就是因爲其新增了二進制分幀層。

幀(frame)包含部分:類型Type, 長度Length, 標記Flags, 流標識Stream和frame payload有效載荷。

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

流是連接中的一個虛擬信道,可以承載雙向消息傳輸。每個流有唯一整數標識符。爲了防止兩端流ID衝突,客戶端發起的流具有奇數ID,服務器端發起的流具有偶數ID。

流標識是描述二進制frame的格式,使得每個frame能夠基於http2發送,與流標識聯繫的是一個流,每個流是一個邏輯聯繫,一個獨立的雙向的frame存在於客戶端和服務器端之間的http2連接中。一個http2連接上可包含多個併發打開的流,這個併發流的數量能夠由客戶端設置。

在二進制分幀層上,http2.0會將所有傳輸信息分割爲更小的消息和幀,並對它們採用二進制格式的編碼將其封裝,新增的二進制分幀層同時也能夠保證http的各種動詞,方法,首部都不受影響,兼容上一代http標準。其中,http1.X中的首部信息header封裝到Headers幀中,而request body將被封裝到Data幀中。

02 多路複用 (Multiplexing) / 連接共享

在http1.1中,瀏覽器客戶端在同一時間,針對同一域名下的請求有一定數量的限制,超過限制數目的請求會被阻塞。這也是爲何一些站點會有多個靜態資源 CDN 域名的原因之一。

而http2.0中的多路複用優化了這一性能。多路複用允許同時通過單一的http/2 連接發起多重的請求-響應消息。有了新的分幀機制後,http/2 不再依賴多個TCP連接去實現多流並行了。每個數據流都拆分成很多互不依賴的幀,而這些幀可以交錯(亂序發送),還可以分優先級,最後再在另一端把它們重新組合起來。

http 2.0 連接都是持久化的,而且客戶端與服務器之間也只需要一個連接(每個域名一個連接)即可。http2連接可以承載數十或數百個流的複用,多路複用意味着來自很多流的數據包能夠混合在一起通過同樣連接傳輸。當到達終點時,再根據不同幀首部的流標識符重新連接將不同的數據流進行組裝。

上圖展示了一個連接上的多個傳輸數據流:客戶端向服務端傳輸數據幀stream5,同時服務端向客戶端亂序發送stream1和stream3。這次連接上有三個響應請求亂序並行交換。

上圖就是http1.X和http2.0在傳輸數據時的區別。以貨物運輸爲例再現http1.1與http2.0的場景:

http1.1過程:貨輪1從A地到B地去取貨物,取到貨物後,從B地返回,然後貨輪2在A返回並卸下貨物後纔開始再從A地出發取貨返回,如此有序往返。

http2.0過程:貨輪1、2、3、4、5從A地無序全部出發,取貨後返回,然後根據貨輪號牌卸載對應貨物。

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

03 頭部壓縮(Header Compression)

http1.x的頭帶有大量信息,而且每次都要重複發送。http/2使用encoder來減少需要傳輸的header大小,通訊雙方各自緩存一份頭部字段表,既避免了重複header的傳輸,又減小了需要傳輸的大小。

對於相同的數據,不再通過每次請求和響應發送,通信期間幾乎不會改變通用鍵-值對(用戶代理、可接受的媒體類型,等等)只需發送一次。

事實上,如果請求中不包含首部(例如對同一資源的輪詢請求),那麼,首部開銷就是零字節,此時所有首部都自動使用之前請求發送的首部。

如果首部發生了變化,則只需將變化的部分加入到header幀中,改變的部分會加入到頭部字段表中,首部表在 http 2.0 的連接存續期內始終存在,由客戶端和服務器共同漸進地更新。

需要注意的是,http 2.0關注的是首部壓縮,而我們常用的gzip等是報文內容(body)的壓縮,二者不僅不衝突,且能夠一起達到更好的壓縮效果。

http/2使用的是專門爲首部壓縮而設計的HPACK②算法。

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

04 壓縮原理

用header字段表裏的索引代替實際的header。

http/2的HPACK算法使用一份索引表來定義常用的http Header,把常用的 http Header 存放在表裏,請求的時候便只需要發送在表裏的索引位置即可。

例如 :method=GET 使用索引值 2 表示,:path=/index.html 使用索引值 5 表示,如下圖:

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

只要給服務端發送一個 Frame,該 Frame 的 Payload 部分存儲 0x8285,Frame 的 Type 設置爲 Header 類型,便可表示這個 Frame 屬於 http Header,請求的內容是:

1 GET /index.html

爲什麼是 0x8285,而不是 0x0205?這是因爲高位設置爲 1 表示這個字節是一個完全索引值(key 和 value 都在索引中)。

類似的,通過高位的標誌位可以區分出這個字節是屬於一個完全索引值,還是僅索引了 key,還是 key和value 都沒有索引(參見:HTTP/2首部壓縮的OkHttp3實現④)。

因爲索引表的大小的是有限的,它僅保存了一些常用的 http Header,同時每次請求還可以在表的末尾動態追加新的 http Header 緩存,動態部分稱之爲 Dynamic Table。Static Table 和 Dynamic Table 在一起組合成了索引表:

HPACK 不僅僅通過索引鍵值對來降低數據量,同時還會將字符串進行霍夫曼編碼來壓縮字符串大小。

以常用的 User-Agent 爲例,它在靜態表中的索引值是 58,它的值是不存在表中的,因爲它的值是多變的。第一次請求的時候它的 key 用 58 表示,表示這是一個 User-Agent ,它的值部分會進行霍夫曼編碼(如果編碼後的字符串變更長了,則不採用霍夫曼編碼)。

服務端收到請求後,會將這個 User-Agent 添加到 Dynamic Table 緩存起來,分配一個新的索引值。客戶端下一次請求時,假設上次請求User-Agent的在表中的索引位置是 62, 此時只需要發送 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 只需要發送索引值,新的 Header 會重新加入 Dynamic Table。

05 請求優先級(Request Priorities)

把http消息分爲很多獨立幀之後,就可以通過優化這些幀的交錯和傳輸順序進一步優化性能。每個流都可以帶有一個31比特的優先值:0 表示最高優先級;2的31次方-1 表示最低優先級。

服務器可以根據流的優先級,控制資源分配(CPU、內存、帶寬),而在響應數據準備好之後,優先將最高優先級的幀發送給客戶端。高優先級的流都應該優先發送,但又不會絕對的。絕對地準守,可能又會引入首隊阻塞的問題:高優先級的請求慢導致阻塞其他資源交付。

分配處理資源和客戶端與服務器間的帶寬,不同優先級的混合也是必須的。客戶端會指定哪個流是最重要的,有一些依賴參數,這樣一個流可以依賴另外一個流。優先級別可以在運行時動態改變,當用戶滾動頁面時,可以告訴瀏覽器哪個圖像是最重要的,你也可以在一組流中進行優先篩選,能夠突然抓住重點流。

●優先級最高:主要的html

●優先級高:CSS文件

●優先級中:js文件

●優先級低:圖片

06 服務端推送(Server Push)

服務器可以對一個客戶端請求發送多個響應,服務器向客戶端推送資源無需客戶端明確地請求。並且,服務端推送能把客戶端所需要的資源伴隨着index.html一起發送到客戶端,省去了客戶端重複請求的步驟。

正因爲沒有發起請求,建立連接等操作,所以靜態資源通過服務端推送的方式可以極大地提升速度。Server Push 讓 http1.x 時代使用內嵌資源的優化手段變得沒有意義;如果一個請求是由你的主頁發起的,服務器很可能會響應主頁內容、logo 以及樣式表,因爲它知道客戶端會用到這些東西,這相當於在一個 HTML 文檔內集合了所有的資源。

不過與之相比,服務器推送還有一個很大的優勢:可以緩存!也讓在遵循同源的情況下,不同頁面之間可以共享緩存資源成爲可能。

注意兩點:

1、推送遵循同源策略;

2、這種服務端的推送是基於客戶端的請求響應來確定的。

當服務端需要主動推送某個資源時,便會發送一個 Frame Type 爲 PUSH_PROMISE 的 Frame,裏面帶了 PUSH 需要新建的 Stream ID。意思是告訴客戶端:接下來我要用這個 ID 向你發送東西,客戶端準備好接着。客戶端解析 Frame 時,發現它是一個 PUSH_PROMISE 類型,便會準備接收服務端要推送的流。

http2.0性能瓶頸

啓用http2.0後會給性能帶來很大的提升,但同時也會帶來新的性能瓶頸。因爲現在所有的壓力集中在底層一個TCP連接之上,TCP很可能就是下一個性能瓶頸,比如TCP分組的隊首阻塞問題,單個TCP packet丟失導致整個連接阻塞,無法逃避,此時所有消息都會受到影響。未來,服務器端針對http 2.0下的TCP配置優化至關重要。

01 如何升級http2.0協議

nginx服務器升級http2.0協議需要滿足如下條件:

1、nginx版本高於1.9.5;

2、--with-http_ssl_module 跟 --with-http_v2_module

--with-http_ssl_module模塊是因爲http2.0協議是一種https協議。

02

查看你的nginx配置

nginx -V

這個是已經添加了對應模塊。沒有這兩個模塊的需要手動編譯安裝。

03

找到nginx文件目錄

04 編譯安裝nginx文件

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

然後執行如下命令,進行編譯安裝。

1 make
2 make install

05 更改nginx配置

安裝結束後將nginx.config文件中443端口添加http2;

06

啓動nginx

最後一步,重啓nginx nginx restart(注意不要直接 nginx -s reload )。這時候你的站點就升級爲了http2.0協議了。

檢測

升級完成後,怎麼確定自己的站點是http2.0協議呢?一般有如下幾種方法:

●chrome devtool

打開chrome調試工具,在network勾選protocol項,h2代表的是http2.0協議,可以看到筆者的網站已經都升級好了;

●網站

SSL lab⑤一個SSL服務器檢測的網站,對網站進行安全評級,並將檢測結果自動生成一個詳細的評價報告;

●插件

http/2 and SPDY indicator 這是一款檢測http2.0和SPDY協議(Google開發的基於TCP的會話層協議)的插件。

參考資料:

[1].https://http2.github.io/

[2].http://http2.github.io/http2-spec/compression.html

[3].http://http2.github.io/http2-spec/compression.html#rfc.section.A

[4].https://neyoufan.github.io/2017/01/06/android/OkHttp3中的HTTP2首部壓縮/

[5].https://www.ssllabs.com/ssltest/ana

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章