HTTP/2 技術調研和性能分析

轉載自 | 小米運維(公衆號 ID:MI-SRE)
公衆號二維碼html

HTTP/2 協議簡介

HTTP/2 是現行 HTTP 協議(HTTP/1.x)的替代,但它不是重寫,HTTP 方法 / 狀態碼 / 語義都與 HTTP/1.x 同樣。不過,HTTP/2 修改了數據格式化(分幀)以及在客戶端與服務器間傳輸的方式。HTTP/2 基於 SPDY3,專一於性能,最大的一個目標是在用戶和網站間只用一個鏈接。前端

HTTP/2 經過支持完整的請求與響應複用來減小延遲,經過有效壓縮 HTTP 報頭字段將協議開銷降至最低,同時增長對請求優先級和服務器推送的支持。java

HTTP/2 協議由如下兩個 RFC 組成:nginx

  • RFC 7540 - Hypertext Transfer Protocol Version 2 (HTTP/2); RFC 7541 -
  • HPACK: Header Compression for HTTP/2;

HTTP/2 解決的問題

影響一個 HTTP 網絡請求的因素主要有兩個:帶寬和延遲。在當今的網絡狀況下,帶寬通常再也不是瓶頸,因此咱們主要討論下延遲。延遲通常由下面幾個因素所形成:git

  • 瀏覽器線頭阻塞(Head-Of-Line Blocking):瀏覽器會由於一些緣由阻塞請求。
  • DNS 查詢。
  • 創建鏈接(Initial connection):HTTP 基於 TCP 協議,TCP 的 3 次握手和慢啓動極大增長延遲。

HTTP/1.x 中存在的問題

鏈接沒法複用
鏈接沒法複用會致使每次請求都經歷三次握手和慢啓動。三次握手在高延遲的場景下影響較明顯,慢啓動則對文件類大請求影響較大。github

  • HTTP/1.0 傳輸數據時,每次都須要從新創建鏈接,增長延遲。
  • HTTP/1.1 雖然加入 keep-alive,能夠複用一部分鏈接,但域名分片等狀況下仍然須要創建多個 connection,耗費資源,給服務器帶來性能壓力。

線頭阻塞(Head-Of-Line Blocking)
致使帶寬沒法被充分利用,以及後續健康請求被阻塞。HOLB 是指在 HTTP/1.x 中,因爲服務器必須按接受請求的順序發送響應的規則限制,那麼假設瀏覽器在一個(tcp)鏈接上發送了兩個請求,那麼服務器必須等第一個請求響應完畢才能發送第二個響應——HOLB。雖然如今瀏覽器容許每一個 origin 創建 6 個 connection,但大量網頁動輒幾十個或上百個資源,HOLB 依然是主要問題。
協議開銷大:
HTTP/1.x 中 header 內容過大(每次請求 header 基本不怎麼變化),增長了傳輸的成本。
安全因素:
在 HTTP 中傳輸的內容都是明文,客戶端和服務端雙方沒法驗證身份。web

HTTP/2 解決的問題

鏈接複用:
在用戶和網站之間只用一個鏈接,避免後續創建鏈接過程當中的幾個往返和慢啓動,同時減小了服務器的資源消耗。算法

沒有線頭阻塞:
採用新的二進制分幀層的機制,組成消息的幀能夠亂序發送,幀到達對端從新組裝,不須要等待前面的幀到達後再發送。chrome

報頭壓縮:
HTTP/2 協議中採用 HPACK 來壓縮請求頭和響應頭,下降協議開銷。數據庫

更加安全:
當前主流瀏覽器,都只支持基於 HTTPS 部署的 HTTP/2。

HTTP/2 協議中的新特性

二進制分幀層

HTTP 2.0 性能加強的核心,全在於新增的二進制分幀層,它定義瞭如何封裝 HTTP 消息並在客戶端和服務器之間傳輸,和 HTTP/1.x 對好比下圖:
圖片描述

這裏所謂的 「層」,指的是位於套接字接口與應用可見的高級 HTTP API 之間一個通過優化的新編碼機制:HTTP 的語義(包括各類動詞、方法、標頭)都不受影響,不一樣的是傳輸期間對它們的編碼方式變了。HTTP/1.x 協議以換行符做爲純文本的分隔符,而 HTTP/2 將全部傳輸的信息分割爲更小的消息和幀,並採用二進制格式對它們編碼。
這樣一來,客戶端和服務器爲了相互理解,都必須使用新的二進制編碼機制:HTTP/1.x 客戶端沒法理解只支持 HTTP/2 的服務器,反之亦然。不過沒關係,現有的應用沒必要擔憂這些變化,由於客戶端和服務器會替咱們完成必要的分幀工做。

數據流、消息和幀

新的二進制分幀機制改變了客戶端與服務器之間交換數據的方式。 爲了說明這個過程,咱們須要瞭解 HTTP/2 的三個概念:

  • 數據流:已創建的鏈接內的雙向字節流,能夠承載一條或多條消息。
  • 消息:與邏輯請求或響應消息對應的完整的一系列幀。
  • 幀:HTTP/2 通訊的最小單位,每一個幀都包含幀頭,至少也會標識出當前幀所屬的數據流。

這些概念的關係總結以下:

  • 全部通訊都在一個 TCP 鏈接上完成,此鏈接能夠承載任意數量的雙向數據流。
  • 每一個數據流都有一個惟一的標識符和可選的優先級信息,用於承載雙向消息。
  • 每條消息都是一條邏輯 HTTP 消息(例如請求或響應),包含一個或多個幀。

幀是最小的通訊單位,承載着特定類型的數據,例如 HTTP 標頭、消息負載等等。 來自不一樣 數據流的幀能夠交錯發送,而後再根據每一個幀頭的數據流標識符從新組裝,以下圖:
圖片描述

簡言之,HTTP/2 將 HTTP 協議通訊分解爲二進制編碼幀的交換,這些幀對應着特定數據流中的消息。全部這些都在一個 TCP 鏈接內複用。這是 HTTP/2 協議全部其餘功能和性能優化的基礎。

多路複用

在 HTTP/1.x 中,若是客戶端要想發起多個並行請求以提高性能,則必須使用多個 TCP 鏈接。這是 HTTP/1.x 交付模型的直接結果,該模型能夠保證每一個鏈接每次只交付一個響應(響應排隊)。更糟糕的是,這種模型也會致使隊首阻塞,從而形成底層 TCP 鏈接的效率低下。
HTTP/2 中新的二進制分幀層突破了這些限制,實現了完整的請求和響應複用:客戶端和服務器能夠將 HTTP 消息分解爲互不依賴的幀,而後交錯發送,最後再在另外一端把它們從新組裝起來。
圖片描述

上圖捕捉了同一個鏈接內並行的多個數據流。客戶端正在向服務器傳輸一個 DATA 幀(數據流 5),與此同時,服務器正向客戶端交錯發送數據流 1 和數據流 3 的一系列幀。所以,一個鏈接上同時有三個並行數據流。
將 HTTP 消息分解爲獨立的幀,交錯發送,而後在另外一端從新組裝是 HTTP 2 最重要的一項加強。事實上,這個機制會在整個網絡技術棧中引起一系列連鎖反應,從而帶來巨大的性能提高,讓咱們能夠:

  • 並行交錯地發送多個請求,請求之間互不影響。
  • 並行交錯地發送多個響應,響應之間互不干擾。
  • 使用一個鏈接並行發送多個請求和響應。
  • 沒必要再爲繞過 HTTP/1.x 限制而作不少工做(對 HTTP/1.x 進行優化,例如級聯文件、image sprites 和域名分片。
  • 消除沒必要要的延遲和提升現有網絡容量的利用率,從而減小頁面加載時間。
  • 等等…

HTTP/2 中的新二進制分幀層解決了 HTTP/1.x 中存在的隊首阻塞問題,也消除了並行處理和發送請求及響應時對多個鏈接的依賴。結果,應用速度更快、開發更簡單、部署成本更低。

請求重置

HTTP 1.1 的有一個缺點是:當一個含有確切值的 Content-Length 的 HTTP 消息被送出以後,你就很難中斷它了。固然,一般你能夠斷開整個 TCP 鏈接(但也不老是能夠這樣),但這樣致使的代價就是須要經過三次握手來從新創建一個新的 TCP 鏈接。
一個更好的方案是隻終止當前傳輸的消息並從新發送一個新的。在 HTTP/2 裏面,咱們能夠經過發送 RST_STREAM 幀來實現這種需求,從而避免浪費帶寬和中斷已有的鏈接。

請求優先級

將 HTTP 消息分解爲不少獨立的幀以後,咱們就能夠複用多個數據流中的幀,客戶端和服務器交錯發送和傳輸這些幀的順序就成爲關鍵的性能決定因素。爲了作到這一點,HTTP/2 標準容許每一個數據流都有一個關聯的權重和依賴關係:

  • 能夠向每一個數據流分配一個介於 1 至 256 之間的整數。
  • 每一個數據流與其餘數據流之間能夠存在顯式依賴關係。

數據流依賴關係和權重的組合讓客戶端能夠構建和傳遞 「優先級樹」,代表它傾向於如何接收響應。反過來,服務器可使用此信息經過控制 CPU、內存和其餘資源的分配設定數據流處理的優先級,在資源數據可用以後,帶寬分配能夠確保將高優先級響應以最優方式傳輸至客戶端。
圖片描述

如上圖,HTTP/2 內的數據流依賴關係經過將另外一個數據流的惟一標識符做爲父項引用進行聲明;若是忽略標識符,相應數據流將依賴於 「根數據流」。聲明數據流依賴關係指出,應儘量先向父數據流分配資源,而後再向其依賴項分配資源。換句話說,「請先處理和傳輸響應 D,而後再處理和傳輸響應 C」。

共享相同父項的數據流(即,同級數據流)應按其權重比例分配資源。 例如,若是數據流 A 的權重爲 12,其同級數據流 B 的權重爲 4,那麼要肯定每一個數據流應接收的資源比例,請執行如下操做:
將全部權重求和:4 + 12 = 16
將每一個數據流權重除以總權重:A = 12/16, B = 4/16 所以,數據流 A 應得到四分之三的可用資源,數據流 B 應得到四分之一的可用資源;數據流 B 得到的資源是數據流 A 所獲資源的三分之一。 咱們來看一下上圖中的其餘幾個動手示例:順序爲從左到右:

數據流 A 和數據流 B 都沒有指定父依賴項,依賴於顯式 「根數據流」;A 的權重爲 12,B 的權重爲 4。所以,根據比例權重:數據流 B 得到的資源是 A 所獲資源的三分之一。
   數據流 D 依賴於根數據流;C 依賴於 D。所以,D 應先於 C 得到完整資源分配。權重不重要,由於 C 的依賴關係擁有更高的優先級。
   數據流 D 應先於 C 得到完整資源分配;C 應先於 A 和 B 得到完整資源分配;數據流 B 得到的資源是 A 所獲資源的三分之一。
   數據流 D 應先於 E 和 C 得到完整資源分配;E 和 C 應先於 A 和 B 得到相同的資源分配;A 和 B 應基於其權重得到比例分配。

如上面的示例所示,數據流依賴關係和權重的組合明確表達了資源優先級,這是一種用於提高瀏覽性能的關鍵功能,網絡中擁有多種資源類型,它們的依賴關係和權重各不相同。不只如此,HTTP/2 協議還容許客戶端隨時更新這些優先級,進一步優化了瀏覽器性能。換句話說,咱們能夠根據用戶互動和其餘信號更改依賴關係和從新分配權重。

注:數據流依賴關係和權重表示傳輸優先級,而不是要求,所以不能保證特定的處理或傳輸順序。即,客戶端沒法強制服務器經過數據流優先級以特定順序處理數據流。 儘管這看起來違反直覺,但倒是一種必要行爲。 咱們不但願在優先級較高的資源受到阻止時,還阻止服務器處理優先級較低的資源。

每一個來源一個鏈接

有了新的分幀機制後,HTTP/2 再也不依賴多個 TCP 鏈接去並行複用數據流;每一個數據流都拆分紅不少幀,而這些幀能夠交錯,還能夠分別設定優先級。所以,全部 HTTP/2 鏈接都是永久的,並且僅須要每一個來源一個鏈接,隨之帶來諸多性能優點。
大多數 HTTP 傳輸都是短暫且急促的,而 TCP 則針對長時間的批量數據傳輸進行了優化。 經過重用相同的鏈接,HTTP/2 既能夠更有效地利用每一個 TCP 鏈接,也能夠顯著下降總體協議開銷。不只如此,使用更少的鏈接還能夠減小佔用的內存和處理空間,也能夠縮短完整鏈接路徑(即,客戶端、可信中介和源服務器之間的路徑)這下降了總體運行成本並提升了網絡利用率和容量。 所以,遷移到 HTTP/2 不只能夠減小網絡延遲,還有助於提升通量和下降運行成本。

在 HTTP/2 RFC 文檔中建議實現時客戶端不該該在給定的目的地上打開多個 HTTP/2 鏈接,目的地是由給定的 URI 肯定的 IP 地址及 TCP 端口,或者配置的代理 IP 和端口。固然客戶端可使用不相同的服務端名稱標識值或者提供不同的 ssl 證書對相同的 IP 地址及 TCP 端口打開多個鏈接,但應該避免對相同的配置上建立多個鏈接。
注:鏈接數量減小對提高 HTTPS 部署的性能來講是一項特別重要的功能:能夠減小開銷較大的 TLS 鏈接數、提高會話重用率,以及從總體上減小所需的客戶端和服務器資源。

流量控制

流量控制是一種阻止發送方向接收方發送大量數據的機制,以避免超出後者的需求或處理能力:發送方可能很是繁忙、處於較高的負載之下,也可能僅僅但願爲特定數據流分配固定量的資源。例如,客戶端可能請求了一個具備較高優先級的大型視頻流,可是用戶已經暫停視頻,客戶端如今但願暫停或限制從服務器的傳輸,以避免提取和緩衝沒必要要的數據。再好比,一個代理服務器可能具備較快的下游鏈接和較慢的上游鏈接,而且也但願調節下游鏈接傳輸數據的速度以匹配上游鏈接的速度來控制其資源利用率;等等。

不過,因爲 HTTP/2 數據流在一個 TCP 鏈接內複用,TCP 流控制既不夠精細,也沒法提供必要的應用級 API 來調節各個數據流的傳輸。爲了解決這一問題,HTTP/2 提供了一組簡單的構建塊,這些構建塊容許客戶端和服務器實現其本身的數據流和鏈接級流量控制:

  • 流量控制具備方向性,每一個接收方均可以根據自身須要選擇爲每一個數據流和整個鏈接設置任意的窗口大小。
  • 流量控制基於窗口更新幀進行,即接收方廣播本身準備接收某個數據流的多少字節,以及對整個鏈接要接收多少字節。
  • 流量控制窗口大小經過 WINDOW_UPDATE 幀更新,這個字段制定了流 ID 和窗口遞增值。
  • 流量控制能夠由接收方禁用,包括針對個別的流和針對整個鏈接。
  • 流量控制基於每一跳進行,而非端到端控制。即,可信中介可使用它來控制資源使用,以及基於自身條件和啓發式算法實現資源分配機制。

HTTP/2 未指定任何特定算法來實現流量控制。不過,它提供了簡單的構建塊並推遲了客戶端和服務器實現,能夠實現自定義策略來調節資源使用和分配,以及實現新傳輸能力,同時提高網絡應用的實際性能和感知性能。

例如,應用層流量控制容許瀏覽器僅提取一部分特定資源,經過將數據流流控制窗口減少爲零來暫停提取,稍後再行恢復。換句話說,它容許瀏覽器提取圖像預覽或首次掃描結果,進行顯示並容許其餘高優先級提取繼續,而後在更關鍵的資源完成加載後恢復提取。

服務器推送

HTTP/2 新增的另外一個強大的新功能是,服務器能夠對一個客戶端請求發送多個響應。 換句話說,除了對最初請求的響應外,服務器還能夠向客戶端推送額外資源(以下圖),而無需客戶端明確地請求。
圖片描述

注:HTTP/2 打破了嚴格的請求 - 響應語義,支持一對多和服務器發起的推送工做流,在瀏覽器內外開啓了全新的互動可能性。這是一項使能功能,對咱們思考協議、協議用途和使用方式具備重要的長期影響。

爲何在瀏覽器中須要一種此類機制呢?一個典型的網絡應用包含多種資源,客戶端須要檢查服務器提供的文檔才能逐個找到它們。那爲何不讓服務器提早推送這些資源,從而減小額外的延遲時間呢?服務器已經知道客戶端下一步要請求什麼資源,這時候服務器推送便可派上用場。

事實上,若是在網頁中內聯過 CSS、JavaScript,或者經過數據 URI 內聯過其餘資產,那麼就已經親身體驗過服務器推送了。對於將資源手動內聯到文檔中的過程,咱們其實是在將資源推送給客戶端,而不是等待客戶端請求。使用 HTTP/2,咱們不只能夠實現相同結果,還會得到其餘性能優點。 推送資源能夠進行如下處理:

  • 由客戶端緩存
  • 在不一樣頁面之間重用
  • 與其餘資源一塊兒複用
  • 由服務器設定優先級
  • 被客戶端拒絕

PUSH_PROMISE 101

全部服務器推送數據流都由 PUSH_PROMISE 幀發起,代表了服務器向客戶端推送所述資源的意圖,而且須要先於請求推送資源的響應數據傳輸。這種傳輸順序很是重要:客戶端須要瞭解服務器打算推送哪些資源,以避免爲這些資源建立重複請求。知足此要求的最簡單策略是先於父響應(即,DATA 幀)發送全部 PUSH_PROMISE 幀,其中包含所承諾資源的 HTTP 標頭。
在客戶端接收到 PUSH_PROMISE 幀後,它能夠根據自身狀況選擇拒絕數據流(經過 RST_STREAM 幀)。 (若是資源已經位於緩存中,可能會發生這種狀況。) 這是一個相對於 HTTP/1.x 的重要提高。 相比之下,使用資源內聯(一種受歡迎的 HTTP/1.x「優化」)等同於 「強制推送」:客戶端沒法選擇拒絕、取消或單獨處理內聯的資源。
使用 HTTP/2,客戶端仍然徹底掌控服務器推送的使用方式。客戶端能夠限制並行推送的數據流數量;調整初始的流控制窗口以控制在數據流首次打開時推送的數據量;或徹底停用服務器推送。這些優先級在 HTTP/2 鏈接開始時經過 SETTINGS 幀傳輸,可能隨時更新。
推送的每一個資源都是一個數據流,與內嵌資源不一樣,客戶端能夠對推送的資源逐一複用、設定優先級和處理。 瀏覽器強制執行的惟一安全限制是,推送的資源必須符合原點相同這一政策:服務器對所提供內容必須具備權威性。

報頭壓縮

每一個 HTTP 傳輸都承載一組報頭,這些報頭說明了傳輸的資源及其屬性。 在 HTTP/1.x 中,此元數據始終以純文本形式,一般會給每一個傳輸增長 500–800 字節的開銷。若是使用 HTTP Cookie,增長的開銷有時會達到上千字節。爲了減小此開銷和提高性能, HTTP/2 使用 HPACK 壓縮格式壓縮請求和響應標頭元數據,這種格式採用兩種簡單可是強大的技術:

  1. 這種格式支持經過靜態 Huffman 編碼對傳輸的標頭字段進行編碼,從而減少了各個傳輸的大小。
  2. 這種格式要求客戶端和服務器同時維護和更新一個包含以前見過的標頭字段的索引列表(換句話說,它能夠創建一個共享的壓縮上下文),此列表隨後會用做參考,對以前傳輸的值進行有效編碼。

利用 Huffman 編碼,能夠在傳輸時對各個值進行壓縮,而利用以前傳輸值的索引列表,咱們能夠經過傳輸索引值的方式對重複值進行編碼,索引值可用於有效查詢和重構完整的標頭鍵值對。
圖片描述

做爲一種進一步優化方式,HPACK 壓縮上下文包含一個靜態表和一個動態表:靜態表在規範中定義,並提供了一個包含全部鏈接均可能使用的經常使用 HTTP 標頭字段(例如,有效標頭名稱)的列表;動態表最初爲空,將根據在特定鏈接內交換的值進行更新。所以,爲以前未見過的值採用靜態 Huffman 編碼,並替換每一側靜態表或動態表中已存在值的索引,能夠減少每一個請求的大小。
注:在 HTTP/2 中,請求和響應標頭字段的定義保持不變,僅有一些微小的差別:全部標頭字段名稱均爲小寫,請求行如今拆分紅各個 :method、:scheme、:authority 和 :path 僞標頭字段。

至於 HPACK 壓縮的詳細介紹,請點擊這裏:HTTP/2 頭部壓縮技術介紹或者官方 RFC

HTTP/2 協議實際性能測試與分析

爲了測試 HTTP/2 對 web 訪問的性能提高,本人藉助 bbs 產品線的 miui 官方網站,開啓了 tengine 的 HTTP/2 的支持,取一週的訪問數據與 HTTPS、HTTP 訪問數據進行對比分析,詳細結果以下:

採集數據量

響應類型分佈

請求類別 http https http2
2xx 945144 927482 505702
3xx 243075 258331 681997
4xx 2372 4750 2813
5xx 9 37 88
sum 1190600 1190600 1190600

延遲數據分析

2xx 請求各個響應時間段佔比(基於 nginx log 的 request_time 數據)

時間 http https http2
<50ms 79.75% 78.89% 82.60%
<100ms 87.24% 87.47% 89.86%
<150ms 91.12% 91.90% 93.19%
<200ms 92.72% 93.82% 94.54%
<2s 98.99% 99.71% 99.48%

2xx 請求響應時間大於 7 秒的數量 (基於 nginx log 的 request_time 數據)

時間 http https http2
>7s 1960(0.207%) 603(0.065%) 950(0.187%)
>10s 1519(0.160%) 420(0.045%) 638(0.126%)
>30s 594(0.062%) 165(0.017%) 190(0.037%)
>60s 259(0.027%) 104(0.011%) 97(0.019%)

2xx 請求後端響應時間超過 7 秒的數量(基於 nginx log 的 upstream_response_time)

時間 http https http2
>7s 68(0.007%) 88(0.017%) 58(0.006%)

帶寬數據分析

301 請求響應大小(基於 nginx 的 request_length 和 bytes_sent 數據)

/static/image/common/miui9.jpg 請求數 總大小(byte) avg(byte) 請求總大小(byte) 請求avg(byte)
http 2722 1460146 536 3194589 1173
https 4695 2618278 415 8019924 1708
http2 16239 6751609 557 1209570 74

200 請求響應大小(基於 nginx log 的 request_length 和 bytes_sent 數據)

/favicon.ico 請求數 響應總大小(byte) 響應avg(byte) 請求總大小(byte) 請求avg(byte)
http 17658 23552229 1413 15300656 866
https 117178 165623779 1413 122356406 1044
http2 80856 105496656 1304 10015908 123

來源分析

客戶端分析(基於 nginx log 的 user_agent 數據)

協議 chrome MSIE safari FireFox Crawlers others unknown
http 23.53% 4.26% 3.56% 0.67% 4.01% 15.07% 48.22%
https 79.24% 5.93% 3.67% 1.54% 7.87% 1.42% 0.1%
http2 88.57% 4.93% 1.91% 1.57% 0% 2.9% 0.03%

平臺分析(基於 nginx log 的 user_agent 數據)

協議 Android Windows Linux IOS Darwin unknown
http 28.4% 16.07% 0.29% 2.46% 0.12% 51.86%
https 64.85% 24.65% 0.62% 0.54% 0.29% 9.05%
http2 40.06% 57.62% 1.49% 0.44% 0% 0.02%

結論

  1. 典型網絡 RTT 下(50~200ms),HTTP/2 對網絡延遲性能有必定程度的提高,優於 HTTP/1.x 和 HTTPS。
  2. 在 2xx 和 3xx 請求下 HTTP/2 利用 HPACK 機制壓縮響應頭和請求頭,有效下降了請求和響應的大小,節省了流量,下降了協議消耗。
  3. 目前 HTTP/2 大部分流量來自於 chrome,其對 HTTP/2 的支持性也是最好的。

HTTP/2 協議部署建議

建議一:如今是否須要遷移到 HTTP/2

實現 HTTP/2 很簡單,不過,HTTP/2 並非萬能的銀彈,它只對某些 Web 應用有用,對另一些則沒那麼有用。

若是你使用 SSL/TLS(之後簡稱 TLS),那麼 HTTP/2 能夠提高網站性能。若是你沒有,那在使用 HTTP/2 以前要先支持 TLS。這時候,使用 TLS 的性能損耗大體能夠被使用 HTTP/2 的性能提高抵銷。不過仍是建議你在實際應用以前先測試一下。
HTTP/2 有五大優點:

  1. 每一個服務器只用一個鏈接。HTTP/2 對每一個服務器只使用一個鏈接,而不是每一個文件一個鏈接。這樣,就省掉了屢次創建鏈接的時間,這個時間對 TLS 尤爲明顯,由於 TLS 鏈接費時間。
  2. 加速 TLS 交付。HTTP/2 只需一次耗時的 TLS 握手,而且經過一個鏈接上的多路利用實現最佳性能。HTTP/2 還會壓縮首部數據,省掉 HTTP/1.x 時代所需的一些優化工做,好比拼接文件,從而提升緩存利用率。
  3. 簡化 Web 應用。使用 HTTP/2 可讓 Web 開發者省不少事,由於不用再作那些針對 HTTP/1.x 的優化工做了。
  4. 適合內容混雜的頁面。HTTP/2 特別適合混合了 HTML、CSS、JavaScript、圖片和有限多媒體的傳統頁面。瀏覽器能夠優先安排那些重要的文件請求,讓頁面的關鍵部分先出現,快出現。
  5. 更安全。經過減小 TLS 的性能損失,可讓更多應用使用 TLS,從而讓用戶信息更安全。

圖片描述

相應地,HTTP/2 也有五個不足之處。

  1. 單鏈接開銷比較大。HPACK 數據壓縮算法會更新兩端的查找表。這樣可讓鏈接有狀態,而破壞狀態就意味着要重建查找表,另外單鏈接佔用內存較多。
  2. 你可能不須要 SSL。若是你的數據不須要保護,或者已經使用 DRM 或其餘編碼進行保護了,那麼 TLS 的安全性對你可能無所謂。
  3. 須要拋棄針對 HTTP/1.x 的優化。HTTP/1.x 優化在支持 HTTP/2 的瀏覽器中會影響性能,所以可能須要花時間把它們推倒重來。
  4. 對下載大文件不利。若是你的應用主要提供大文件下載或者流媒體播放,那可能不想用 TLS,並且在只有一個流的狀況下,多路複用也體現不出什麼優點。
  5. 你的客戶也許不在意。你的客戶極可能不在意他分享的自家貓咪的視頻是否受到 TLS 和 HTTP/2 的保護。

總之,一切要看性能。這方面,有好消息也有壞消息。
好消息是 nginx 官方團隊在內部對 NGINX 作過測試,結果從理論上可以獲得印證:對於要經過典型網絡延遲請求的混合內容網頁,HTTP/2 的性能好於 HTTP/1.x 和 HTTPS。基於鏈接的 RTT,結果能夠分三種狀況。

  • 很低的 RTT(0-20ms):HTTP/1.x、HTTP/2 和 HTTPS 基本無差異。
  • 典型網絡 RTT(30-250ms):HTTP/2 比 HTTP/1.x 快,並且它們都比 HTTPS 快。美國兩個相鄰城市間的 RTT 約爲 30 ms,而東西海岸間(約 3000 英里)則約爲 70 ms。東京到倫敦間最短路徑的 RTT 大約 240 ms。
  • 高 RTT(300ms 及以上):HTTP/1.x 比 HTTP/2 快,後者又比 HTTPS 快。

圖片描述

這張圖顯示了首次渲染的時間,也就是用戶第一次在本身屏幕上看到網頁內容的時間。這個時間通常認爲關係到用戶對網站響應速度的感知。

然而,每一個網頁都不相同,實際上每一個用戶的會話也不同。若是你託管流媒體或提供大文件下載,那你的決定可能不同,甚至相反。

建議二:終止 HTTP/2 和 TLS

終止協議意味着客戶端使用指望的協議鏈接代理服務器,好比 TLS 或 HTTP/2,而後代理服務器再去鏈接應用服務器、數據庫服務器等,但不須要使用相同的協議,以下圖所示。
圖片描述

使用獨立的服務器終止協議意味着使用多服務器架構。多服務器多是多個物理服務器、多個虛擬服務器,或者 AWS 這樣的雲環境中的多個虛擬服務器實例。多服務器就比單服務器複雜,或者比應用服務器 / 數據庫服務器的組合複雜。不過,多服務器架構有不少好處,並且不少流量大的網站也必須用這種架構。

配置了服務器或者虛擬服務器以後,不少事情都成爲可能。新服務器能夠分擔其餘服務器的負載,可用於負載平衡、靜態文件緩存和其餘用途。另外,也可讓添加和替換應用服務器或其餘服務器更容易。
NGINX 和 NGINX Plus 常常被用來終止 TLS 和 HTTP/2 協議、負載平衡。已有環境沒必要改動,除非要把 NGINX 服務器挪到前端。

建議三:找出爲 HTTP/1.x 優化的代碼

在決定採用 HTTP/2 以前,首先得知道你的代碼有哪些是針對 HTTP/1.x 優化過的。大概有四方面的優化。

  1. 分域存儲。爲了實現並行請求文件,你可能把文件分散到了不一樣的域裏,CDN 會自動這麼作。但分域存儲會影響 HTTP/2 的性能,建議使用 HTTP/2 友好的分域存儲(建議六),只針對 HTTP/1.x 用戶分域。
  2. 雪碧圖。雪碧圖把不少圖片拼成一個文件,而後經過代碼按需取得每一個圖片。雪碧圖在 HTTP/2 的環境下沒太大用處,但仍是有點用的。
  3. 拼接的代碼文件。與使用雪碧圖的緣由相似,不少獨立的文件也會被弄成一個,而後瀏覽器再從其中找到並運行須要的文件。
  4. 插入行內的文件。CSS 代碼、JavaScript 代碼,甚至圖片等被直接插到 HTML 文件中的內容。這樣能夠減小文件傳輸,代價是初始 HTML 文件較大。

後面三種優化都涉及把小文件塞進一個較大的文件裏,目的是減小新建鏈接的初始化和握手,這些操做對 TLS 而言很是費時間。

第一種優化即分域存儲偏偏相反,強制打開多個鏈接,目的是並行地從不一樣的域獲取文件。這兩種看似矛盾的技術對於 HTTP/1.x 下的站點卻十分有效。然而,要用好這兩種技術,必須投入大量時間、精力和資源,用於實現、管理和運維。

在採用 HTTP/2 以前,須要找出應用了這些優化的代碼,分析一下它們會不會影響你的應用設計和工做流程。這樣在遷移到 HTTP/2 以後,就能夠着手改造它們,甚至撤銷某些優化。

建議四:部署 HTTP/2

事實上,部署 HTTP/2 並不難。若是使用 NGINX,只要在配置文件中啓動相應的協議就能夠了。瀏覽器和服務器會協商採用什麼協議,若是瀏覽器支持 HTTP/2(並且也在使用 TLS),就會使用 HTTP/2。
配置完服務器後,使用支持 HTTP/2 瀏覽器的用戶就會基於 HTTP/2 運行你的應用,而使用舊版本瀏覽器的用戶則會繼續使用 HTTP/1.x 運行你的應用,以下圖所示。若是你的網站流量很是大,那麼應該監測改變先後的性能,對於性能下降的狀況,可能就得撤銷更改。

注意:使用 HTTP/2 及其單鏈接以後,NGINX 某些配置的重要性會很明顯,特別要注意的是 output_buffers、proxy_buffers 和 ssl_buffer_size 等指令,多測試一下。參見 general configuration notes,特定的 SSL 建議,以及 NGINX 關於 SSL 性能的白皮書。
注意:使用 HTTP/2 傳輸密文要格外注意。HTTP/2 的 RFC 中有一個長長的列表,列出了要避免的加密套件。建議你本身也搞一個表格,啓用 ssl_buffer_size,而後在全部經常使用的瀏覽器版本下測試你想用的加密套件。

建議五:再談 HTTP/1.x 優化

你說奇怪不,撤銷和修改針對 HTTP/1.x 優化的代碼竟然是實現 HTTP/2 最有創意的部分。這裏面有幾個問題要注意,由於不少事怎麼作都是能夠的。
在開始運做以前,必須考慮舊版本瀏覽器用戶是否好過。以後,能夠採起三個策略撤銷和修改 HTTP/1.x 的優化。

  • 什麼也不用作。假如你並無針對 HTTP/1.x 作過優化,或者只作過少許優化,那麼你幾乎什麼也不用作,就能夠直接遷移到 HTTP/2。
  • 有選擇地去作。第二種狀況是減小合併某些文件,而不是徹底不合並。好比,牽扯到不少場景的雪碧圖就不用動,而被塞得滿滿的 HTML 可能就要分離出來一些。
  • 徹底撤銷 HTTP/1.x 優化。能夠再也不作之前作過的任何優化。

緩存仍是普適的。理論上,緩存操做很是適合小文件特別多的狀況。可是,小文件多也意味着文件 I/O 多。所以一些相近文件的合併仍是必要的,一方面要考慮工做流程,另外一方面要考慮應用性能。建議多關注一下其餘人在過渡到 HTTP/2 過程當中的一些經驗。

建議六:實現智能分域

分域存儲多是最極端但也最成功的 HTTP/1.x 優化策略。它可以提高 HTTP/1.x 下的應用性能,但在 HTTP/2 之下,其性能提高能夠忽略不講(由於只有一個鏈接。)
對 HTTP/2 友好的分域,要保證如下兩點:

  • 讓多個域名解析到同一個 IP。
  • 確保證書包含通配符,以便全部分域名均可以使用,適當的多域證書固然也能夠。

有了這些保障,分域還會繼續對 HTTP/1.x 有效,即域名仍然能夠觸發瀏覽器建立更多鏈接,但對 HTTP/2 則無效,由於這些域名會被當作同一個域,一個鏈接就能夠訪問全部域名了。

HTTP/2 協議客戶端和服務支持狀況

客戶端

PC 端

若是業務提供的是 web 形式的內容,經過瀏覽器進行訪問,因爲當前大部分的瀏覽器都已經支持 HTTP/2 了,因此基本不需進行任何操做,如下爲支持 HTTP/2 的瀏覽器列表:

瀏覽器 支持HTTP/2 基於的內核 備註
Chrome(49) 支持 從49版本開始支持
IE 11 不支持 win10系統上的IE11支持h2
Edge(14) 支持 從14版本開始支持
Safari(10.1) 支持 從10.1版本開始支持,但都須要OSX10.11+以上系統版本
Firefox(52) 支持 從52版本開始支持
Opera(47.0.2631.55) 支持 從46版本開始支持
搜狗瀏覽器(7.1.5.25209) 支持
獵豹瀏覽器(6.0.114.15532) 支持
2345加速瀏覽器(8.7.0.16013) 支持
百度瀏覽器(8.7.5000.4962) 不支持 chrome 47
QQ瀏覽器(9.6.4) 支持 chrome 53
360瀏覽器(9.1.0.346) 支持 chrome 55
360極速瀏覽器(8.7.0.306) 支持

移動端

安卓 app 基本採用 JAVA 開發,因爲各個應用採用的與服務端通訊的 http 庫各不相同,有的是採用 jdk 自帶的 httpurlconnection 庫和 httpclient 庫,有的用的是安卓系統自帶的 webview 或者 volley(volley 的 HTTP/2 支持依賴於所使用的 httpstack,默認使用 httpurlconection,但如今也有不少開發者使用第三方的 okhttp 做爲 volley 的 httpstack),而有的開發者直接使用的是第三方庫相似於 okhttp,netty 等,這些 http 庫對 http2.0 的支持狀況各不相同。

基於 java 開發的 http 庫對 HTTP/2 的支持狀況以下:

  • Jetty 從 9.3 版本開始支持,須要依賴於 JDK 8 及以上版本
  • Netty 從 4.1 版本開始支持
  • OkHttp 自然支持
  • Vert.xh 從 3.3.0 版本開始支持
  • Firefly 支持

其它

Golang 的 net/http 庫從 Go1.6 版本開始支持 http2,並默認開啓

服務端

名稱 支持的版本 支持的協商機制
Apache HTTP Server 2.4.17+ h2,h2c ALPN,Upgrade,direct
Apache Tomcat 8.5+ h2,h2c ALPN,Upgrade,direct
Nginx h2,h2c ALPN,NPN,direct
Tengine 2.1.2+ h2 ALPN
Twisted h2 NPN,ALPN
Netty h2,h2c ALPN,NPN,Upgrade,direct

在 HTTP/2 的 github 中維護了一份 HTTP/2 協議的實現列表,更加詳細,可供參考。

擴展閱讀

1.NPN 和 ALPN

因爲現有的 URI 結構正在被 HTTP 1.x 使用而不能被更換,因此 HTTP/2 也必須沿用該結構。所以不得不找到一種方式將使用的協議升級至 HTTP/2,好比能夠要求服務器讓它做響應時使用 HTTP/2 來替代舊的協議。

HTTP 1.1 自己就制定過 「升級」 的方案:提供一個首部字段,表示容許服務器在收到舊協議請求的同時,能夠向客戶端發送新協議的響應。但這一方案每每須要花費一次額外的往返通訊來做爲升級的代價。
而這一代價是 SPDY 團隊不想接受的。由於他們只實現了基於 TLS 的 SPDY,因此他們開發了一個 TLS 的擴展去簡化協議的協商。這個擴展被稱做 NPN(Next Protocol Negotiation),藉助於此,服務器會通知客戶端全部它支持的協議,讓客戶端從中選擇一個合適的來進行通信。

IETF 將這個非正式標準 --NPN 進行規範化,從而演變成了 ALPN(Application Layer Protocol Negotiation)。ALPN 會隨着 HTTP/2 的應用被推廣,而 SPDY 的客戶端與服務器則會繼續使用 NPN。
因爲 NPN 先於 ALPN 誕生,而 ALPN 又經歷了一些標準化過程,因此許多早期的 HTTP/2 客戶端和服務器在協商 HTTP/2 時會將這二者同時實現。與此同時,考慮到 SPDY 會使用 NPN,而許多服務器又會同時提供 SPDY 以及 HTTP/2,因此在這些服務器上同時支持 ALPN 以及 NPN 顯然會成爲最理所固然的選擇。
ALPN 和 NPN 的主要區別在於:誰來決定通訊協議。在 ALPN 的描述中,是讓客戶端先發送一個協議優先級列表給服務器,由服務器最終選擇一個合適的。而 NPN 則正好相反,客戶端有着最終的決定權。

ALPN 擴展的具體資料,能夠參考 Jerry Qu 寫的這篇博客:談談 HTTP/2 的協議協商機制

2.QUIC

QUIC (Quick UDP Internet Connection,快速 UDP 互聯網鏈接) 是一個新的基於 UDP 的多路複用且安全的傳輸協議,它從頭開始設計,且爲 HTTP/2 語義作了優化。儘管以 HTTP/2 做爲主要的應用協議而構建,然而 QUIC 的構建是基於傳輸和安全領域數十年的經驗的,且實現了使它成爲有吸引力的現代通用傳輸協議的機制。QUIC 提供了等價於
HTTP/2 的多路複用和流控,等價於 TLS 的安全機制,及等價於 TCP 的鏈接語義、可靠性和擁塞控制。

QUIC 徹底運行於用戶空間,它當前做爲 Chromium 瀏覽器的一部分發布給用戶,以便於快速的部署和實驗。做爲基於 UDP 的用戶空間傳輸協議,QUIC 能夠作一些因爲遺留的客戶端和中間設備,或曠日持久的操做系統開發和部署週期的阻礙,而被證實很難在現有的協議中部署的創新。

QUIC 的一個重要目標是經過快速的實驗得到更好的傳輸設計相關的知識。

基於早期的部署的 QUIC 標準化建議爲 [draft-hamilton-quic-transport-protocol],[draft-shade-quic-http2-mapping],[draft-iyengar-quic-loss-recovery],和 [draft-thomson-quic-tls]。

更加詳細的資料請參考這裏:中文文檔Chromium 的 QUIC 主頁

參考資料

相關文章
相關標籤/搜索