HTTP/2
首先看看HTTP/2的一些新特性:
l 二進制分幀
l 首部壓縮
l 流量控制
l 多路複用
l 請求優先級
l 伺服器推送
二進制分幀
二進制分幀層,是HTTP2.0性能增強的核心。
HTTP 1.x在應用層以純文字的形式進行通信,HTTP2.0在不改變HTTP1.x的語義、方法、狀态碼、URL以及首部字段的情況下,為了突破原有性能限制,在應用層(HTTP)和傳輸層(TCP)之間增加了一個二進制分幀層。HTTP2.0将所有的傳輸資訊分割為更小的消息和幀,并對它們采用二進制格式編碼,如下圖所示。

這裡引入一個新的通信機關:幀。
幀是HTTP 2.0通信的最小機關,包括幀首部、流辨別符、優先值和幀淨荷等。
其中,幀類型可以分為:
l DATA:用于傳輸HTTP消息體
l HEADERS:用于傳輸首部字段
l SETTINGS:用于約定用戶端和服務端的配置資料。比如設定初識的雙向流量控制視窗大小
l WINDOW_UPDATE:用于調整個别流或個别連接配接的流量
l PRIORITY:用于指定或重新指定引用資源的優先級
l RST_STREAM:用于通知流的非正常終止
l PUSH_ PROMISE:服務端推送許可
l PING:用于計算往返時間,執行“ 活性” 檢活
l GOAWAY:用于通知對端停止在目前連接配接中建立流
标志位,用于不同的幀類型定義特定的消息标志。比如DATA幀就可以使用End Stream: true表示該條消息通信完畢;流辨別位表示幀所屬的流ID;優先值用于HEADERS幀,表示請求優先級;R表示保留。
下面是抓包的一個HEADERS幀:
另外一個兩個要說一下的概念:消息和流。
消息是指邏輯上的HTTP消息(請求/響應),一系列資料幀組成一個完整的消息,比如一系列DATA幀和一個HEADERS幀組成了請求消息。
流是連結中的一個虛拟信道,可以承載雙向消息傳輸,每個流有唯一證書辨別符,為了防止兩端流ID沖突,用戶端發起的流具有奇數ID,服務端發起的流具有偶數ID。
所有HTTP 2.0通信都在一個TCP連結上完成,這個連結可以承載任意數量的雙向資料流Stream。相應地,每個資料流以消息的形式發送,而消息由一個或多個幀組成,這些幀可以亂序發送,然後根據每個幀首部的流辨別符重新組裝。
二進制分幀主要是為HTTP2.0其他特性提供基礎。它能把一個資料劃分封裝為更小更便捷的資料。首先是在單鍊多資源方式中,減少服務端的連結壓力,記憶體占用更少,連結吞吐量更大;另一方面,由于TCP連結的減少而使網絡擁塞狀态得以改善,同時慢啟動時間減少,使擁塞和丢包恢複的速度更快。
首部壓縮
HTTP1.x每次通信(請求或響應)都會攜帶首部資訊用于描述資源屬性。而HTTP2.0在用戶端和服務端之間使用首部表來跟蹤和存儲之前發送的鍵值對,首部表在連接配接過程中始終存在,新增的鍵值對會更新到表尾,是以不需要每次通信都攜帶首部,請求與響應首部的定義在HTTP2.0中基本沒有變。
另外HTTP2.0使用了首部壓縮技術,壓縮算法采用HPACK,讓報頭更緊湊、更快速傳輸,有利于移動網絡環境。需要注意的是,HTTP2.0的首部壓縮,與我們常用的gzip等封包内容壓縮不沖突。
流量控制
HTTP/2.0 “流”的流量控制的目标是:在不改變協定的情況下允許使用多種流量控制算法
l 流量控制是特定于一個連接配接的。每種類型的流量控制都是在單獨的一跳的兩個端點之間的,并不是在整個端到端的路徑上的。(這裡的一跳指的是HTTP連接配接的一跳,而不是IP路由的一跳)
l 流量控制是基于WINDOW_UPDATE幀的。接收方公布自己打算在每個流以及整個連接配接上分别接收多少位元組。這是一個以信用為基礎的方案。
l 流量控制是有方向的,由接收者全面控制。接收方可以為每個流和整個連接配接設定任意的視窗大小。發送方必須尊重接收方設定的流量控制限制。客戶方、服務端和中間代理作為接收方時都獨立地公布各自的流量控制視窗,作為發送方時都遵守對端的流量控制設定。
l 無論是新流還是整個連接配接,流量控制視窗的初始值是65535位元組。
l 幀的類型決定了流量控制是否适用于幀。目前,隻有DATA幀服從流量控制,所有其它類型的幀并不消耗流量控制視窗的空間。這保證了重要的控制幀不會被流量控制阻塞。
l 流量控制不能被禁用。
l HTTP/2隻定義了WINDOW_UPDATE幀的格式和語義,并沒有規定接收方如何決定何時發送幀、發送什麼樣的值,也沒有規定發送方如何選擇發送包。具體實作可以選擇任何滿足需求的算法。
多路複用
在HTTP1.1中,浏覽器用戶端在同一時間,針對同一域名下的請求有一定數量的限制。超過限制數目的請求會被阻塞,而HTTP2.0中的多路複用優化了這一性能。
基于二進制分幀層,HTTP2.0可以在共享TCP連接配接的基礎上,同時發送請求和響應。HTTP消息被分解為獨立的幀,而不破壞消息本身的語義,交錯發送出去,最後在另一端根據流ID和首部将他們重新組合。對比看一下HTTP1.x和HTTP2.0,這裡不考慮HTTP1.x的pipeline機制。
HTTP2.0成功解決了HTTP1.x的隊首阻塞問題(TCP層的阻塞仍無法解決),同時,也不需要通過pipeline機制多條TCP連接配接來實作并行請求與響應。減少了TCP連接配接數對伺服器性能有很大提升,同時也消除不必要的延遲,進而減少頁面加載的時間。
請求優先級
把HTTP消息分為很多獨立幀之後,就可以通過優化這些幀的交錯和傳輸順序進一步優化性能。
每個流都可以帶有一個31bit的優先值:0表示最高優先級;2的31次方-1表示最低優先級。
用戶端明确指定優先級,服務端可以根據這個優先級作為互動資料的依據,比如用戶端優先設定為.css>.js>.jpg。服務端按此順序傳回結果更加有利于高效利用底層連接配接,提高使用者體驗。然而,在使用請求優先級時應注意服務端是否支援請求優先級,是否會引起隊首阻塞問題,比如高優先級的 慢響應請求會阻塞其他資源的互動。
伺服器推送
HTTP2.0增加了服務端推送功能,服務端可以根據用戶端的請求,提前傳回多個響應,推送額外的資源給用戶端
如下圖,用戶端請求stream 1(/page.html)。伺服器在傳回stream 1的消息的同時推送了stream 2(/script.js)和stream4(/style.css)
l PUSH_PROMISE幀是服務端向用戶端有意推送資源的信号。
l PUSH_PROMISE幀中隻包含預推送資源的首部。如果用戶端對PUSH_PROMISE幀沒有意見,服務端在PUSH_PROMISE幀後發送響應的DATA幀。如果用戶端已經緩存了該資源,不需要推送,可以拒絕PUSH_PROMISE幀。
l PUSH-PROMISE必須遵循請求-響應原則,隻能借着對請求的響應推送資源。
l PUSH_PROMISE幀必須在傳回響應之前發送,以免用戶端出現競态條件(競态條件是指在多線程的情況下不同的執行順序會導緻計算機執行出不同的結果正确性不同)
l HTTP2.0連接配接後,用戶端與服務端交換SETTINGS幀,借此限定雙向并發的最大數量。是以,用戶端可以限定推送流的數量,或者通過把這個隻設定為0來完全禁止伺服器推送。
l 所有推送的資源都必須遵守同源政策。換句話說,伺服器不能随便将第三方資源推送給用戶端,而必須是經過雙方的确認才行。
HTTP/2現在已經獲得絕大多數浏覽器的支援,不過在使用過程中HTTP/2需要使用1.0.1e之後的openssl版本,通過nginx -V,可以檢視nginx的openssl版本,如果版本低,重新編譯nginx即可。
那麼在nginx中如何配置支援HTTP/2?很簡單,隻需要在server中的listen部分添加http2即可。
怎麼測試http2是否已開啟,方法很多,這裡介紹三種方法:
1、浏覽器開發者工具
2、Chrome擴充HTTP/2 and SPDY indicator
3、指令行用戶端nghttp
另外HTTP/2的伺服器推送,需要nginx配置才能有效利用。
通過http2_push指令配置
這種情況下,demo.html需要用到的資源style.css、image1.jpg和image2.jpg被推送到用戶端。資源少的情況下,我們可以這麼使用,但是資源多的情況下這種方式就不太現實。
自動将資源推送給用戶端
nginx支援攔截link預加載頭的約定,推送這寫頭中辨別的資源,需要在配置中啟動預加載,配置http2_push_preload on
這裡也有一個問題,一般的靜态資源,我們都會設定緩存有效期。當用戶端資源在緩存有效期内的時候,我們強制推送靜态資源,隻會增加伺服器帶寬的壓力,是以我們需要指定用戶端是否需要這些資源,并且不太可能已經緩存過,可能的方法,就是用戶端在首次通路時服務端推送,并在随後的通路請求中包含cookie,服務端通過cookie去判斷是否進行推送,就是有選擇的向用戶端推送資源,配置方法如下:
測試如下: