一 復習與目標
1 復習
- 簡單密碼學、對稱加密與非對稱加密
- 數字簽名、數字證書
- SSL/TLS
- HTTPS = HTTP + SSL/TLS,SSL/TLS為HTTP提供了保密性、完整性和鑒別性
2 目標
- HTTP1.1的問題
- HTTP2.0設計關鍵
- HTTP2.0的詳情
注1:本文並不會講解h2的所有內容,只會包含重要(主觀)的內容。
注2:下一篇再講解具體每個報文類型並進行報文分析。
二 HTTP1.1的問題
(1)HTTP管線化的問題
- 簡單說就是在等待上一個請求響應的同時,發送下一個請求。
- 多個HTTP請求放到一個TCP連接中一一發送,而在發送過程中不需要等待服務器對前一個請求的響應;但是客戶端要按照發送請求的順序來接收響應。
- 隊首阻塞(Head-of-line blocking):如果前一個請求很慢,就會導致后續的請求都受到影響。
- 隊首阻塞的存在,導致瀏覽器一般禁用該功能,服務器也應該禁用掉。
(2)多個TCP連接(6個)
- 由於現在一個網站的請求變多,文件變大,所以HTTP1.1為了提升下載速度,將支持最大6個TCP(Chrome)連接進行HTTP請求。
- 管理一個TCP連接是非常消耗客戶端和瀏覽器的資源,所以需要優化。
(3)分片(多域名)
- 網站的資源進行划分多域名,這樣可以擺脫客戶端對一個域名最大只能打開6個TCP連接的限制。
- 缺點是多個DNS解析的消耗和更多TCP連接的管理
(4)首部太大
- HTTP1.1每次請求都會帶上完整的首部
三 HTTP2設計關鍵
- 降低協議對延遲的敏感
- 修復pipelining和head of line blocking的問題
- 並行操作無需與服務器建立多個連接,從而改進 TCP 的利用率,特別是擁塞控制方面;
- 保持 HTTP 1.1 的語義,利用現有文檔,包括(但不限於)HTTP 方法、狀態碼、URI,以及首部字段
- 明確指出所有新的可擴展機制以及適當的擴展策略。
四 HTTP2協議
(1)二進制分幀層
- 分幀層位於套接字接口與應用可見的高層HTTP API 之間的一個新機制:HTTP 的語義,包括各種動詞、方法、首部,都不受影響,不同的是傳輸期間對它們的編碼方式變了。
- HTTP 1.x 以換行符作為純文本的分隔符,而HTTP2.0 將所有傳輸的信息分割為更小的消息和幀,並對它們采用二進制格式的編碼。
(2)流、消息和幀
-
流:已建立的連接上的雙向字節流。
-
消息:與邏輯消息對應的完整的一系列數據幀
-
幀:通信的最小單位,每個幀包含幀首部,流標識符。
-
總結:
- 所有通信都在一個 TCP 連接上完成。
- 流是連接中的一個虛擬信道,可以承載雙向的消息;每個流都有一個唯一的整數標識符(1、2…N)。
- 消息是指邏輯上的 HTTP 消息,比如請求、響應等,由一或多個幀組成。
- 幀是最小的通信單位,承載着特定類型的數據,如 HTTP 首部、負荷、流標識符,等等。
(3)多向請求和響應
- 概述:客戶端和服務器可以把HTTP 消息分解為互不依賴的幀,然后亂序發送,最后再在另一端把它們重新組合起來。
- 總結:
- 可以並行交錯地發送請求,請求之間互不影響;
- 可以並行交錯地發送響應,響應之間互不干擾;
- 只使用一個TCP連接即可並行發送多個請求和響應;
- 消除不必要的延遲,從而減少頁面加載的時間;
- 不必再為繞過 HTTP 1.x 限制而多做很多工作;
(4)請求優先級
- 每個流都可以帶有一個31 比特的優先值,0~2^31-1,0為最高優先級
- 服務器可以根據流的優先級,控制資源分配(CPU、內存、帶寬),而在響應數據准備好之后,優先將最高優先級的幀發送給客戶端。
注:不同的服務器可能忽略優先級值或者優先級值的處理策略不同。
(5)流量控制
- 由於多個數據流公用同一個TCP連接,那么多個數據流的資源分配尤其重要,於是出現了流的流量控制。
- 流量控制基於每一個虛擬流進行,而非端到端的控制(更細粒度的流量控制);
- 流量控制基於窗口更新幀進行,即接收方廣播自己准備接收某個數據流的多少字節,以及對整個連接要接收多少字節;
- 流量控制窗口大小通過 WINDOW_UPDATE 幀更新,這個字段指定了流 ID 和窗口大小遞增值;
- 流量控制有方向性,即接收方可能根據自己的情況為每個流乃至整個連接設置任意窗口大小;
- 流量控制可以由接收方禁用,包括針對個別的流和針對整個連接。
- 客戶端與服務器交換SETTINGS 幀,目的是設置雙向的流量控制窗口大小。
(6)服務器推送
-
服務器可以對一個客戶端請求發送多個響應。
- 建立HTTP 2.0 連接后,客戶端與服務器交換SETTINGS 幀,借此可以限定雙向並發的流的最大數量。
- 如果並發流最大數量設置為0,代表關閉服務器推送功能。
-
服務器推送類似於資源直接插入到文檔中,就是把資源直接推送給客戶端,而無需客戶端請求。
-
HTTP 2.0 中,唯一的不同就是可以把這個過程從應用中拿出來,放到HTTP 協議本身來實現。
-
好處:
- 客戶端可以緩存推送過來的資源;
- 客戶端可以拒絕推送過來的資源;
- 推送資源可以由不同的頁面共享;
- 服務器可以按照優先級推送資源
-
HTPP1.1嵌入資源都應該改為服務器推送,唯一有必要直接在網頁中插入資源的情況,就是該資源只供那一個網頁使用,而且編碼代價不大。
-
HTTP 2.0服務器推送策略:
- 在自身的代碼中明確發起服務器推送
- 通過額外的 HTTP 首部向服務器發送信號,列出希望推送的資源
- 可以不依賴應用而自動學習相關資源。服務器可以解析文檔,推斷出要推送的資源,或者可以分析流量,然后作出適當的決定。
注:使用上請參考:HTTP/2 服務器推送(Server Push)教程
(7)首部壓縮
- HTTP1.1在每一次通信都會攜帶一組首部,用於描述傳輸的資源及其屬性,長度甚至高達上千字節。
- 為減少這些開銷並提升性能,HTTP 2.0 會壓縮首部元數據:
- HTTP 2.0 在客戶端和服務器端使用“首部表”來跟蹤和存儲之前發送的鍵值對,對於相同的數據,不再通過每次請求和響應發送;
- 首部表在HTTP 2.0的連接存續期內始終存在,由客戶端和服務器共同漸進地更新;
- 每個新的首部鍵值對要么被追加到當前表的末尾,要么替換表中之前的值。
(8)HTTP 2.0升級與發現
- 通過TLS 和 ALPN 發起新的 HTTPS 連接;
- 根據之前的信息發起新的 HTTP 連接:HTTP Upgrade 機制通過協調確定適當的協議。
- 沒有之前的信息而發起新的 HTTP 連接:如果客戶端自己保存有或通過其他手段(如DNS 記錄、手工配置等)獲得了服務器支持HTTP 2.0,可以直接發送HTTP 2.0 分幀,而不必依賴Upgrade 機制。
五 二進制分幀
1 固定首部
- 長度:16位,代表幀凈荷最大可達65535字節(64KB)
- 類型:8位,幀的類型
- DATA:用於傳輸 HTTP 消息體。
- HEADERS:用於傳輸關於流的額外的首部字段。
- PRIORITY:用於指定或重新指定引用資源的優先級。
- RST_STREAM:用於通知流的非正常終止。
- SETTINGS:用於通知兩端通信方式的配置數據。
- PUSH_PROMISE:用於發出創建流和服務器引用資源的要約。
- PING:用於計算往返時間,執行“活性”檢查。
- GOAWAY:用於通知對端停止在當前連接中創建流。
- WINDOW_UPDATE:用於針對個別流或個別連接實現流量控制。
- CONTINUATION:用於繼續一系列首部塊片段。
- 標志:8位,允許不同的幀類型定義特定於幀的消息標志
- R:保留字段,1位
- 流標志符:31位,唯一標識 HTTP 2.0 的流
2 流的建立
(1)流的建立:
- 客戶端通過發送 HEADERS 幀來發起新流,這個幀里包含帶有新流 ID 的公用首部、可選的31 位優先值,以及一組HTTP鍵值對首部;
- 服務器通過發送 PUSH_PROMISE 幀來發起推送流,這個幀與HEADERS幀等效,但它包含“要約流ID”,沒有優先值。
(2)特點
- 流計數器偏置:客戶端發起的流具有奇數ID,服務器發起的流具有偶數ID。
- HEADERS幀或者PUSH_PROMISE幀用於溝通新流的元數據,凈荷會在DATA 幀中單獨發送
(3)完整報頭區塊
- 一個包含報頭終止標記集合的HEADERS 或PUSH_PROMISE幀
- 一個不包含報頭終止標記的HEADERS 或PUSH_PROMISE幀以及一個或多個CONTINUATION幀,最后一個CONTINUATION幀由報頭終止標記
3 流與多路復用
3.1 特點:
- 一個單獨的HTTP/2連接能夠保持多個同時打開的流,各個端點間從多個流中交換幀;
- 流可以被客戶端或者服務端單方面建立、分享或關閉;
- 幀的順序很重要,特別是報頭及數據幀的順序語義上是有意義的;
- 流標識符由啟動流的終端分配。
3.2 流的狀態
+--------+
send PP | | recv PP
,--------| idle |--------.
/ | | \
v +--------+ v
+----------+ | +----------+
| | | send H / | |
,------| reserved | | recv H | reserved |------.
| | (local) | | | (remote) | |
| +----------+ v +----------+ |
| | +--------+ | |
| | recv ES | | send ES | |
| send H | ,-------| open |-------. | recv H |
| | / | | \ | |
| v v +--------+ v v |
| +----------+ | +----------+ |
| | half | | | half | |
| | closed | | send R / | closed | |
| | (remote) | | recv R | (local) | |
| +----------+ | +----------+ |
| | | | |
| | send ES / | recv ES / | |
| | send R / v send R / | |
| | recv R +--------+ recv R | |
| send R / `----------->| |<-----------' send R / |
| recv R | closed | recv R |
`----------------------->| |<----------------------'
+--------+
send: endpoint sends this frame
recv: endpoint receives this frame
H: HEADERS frame (with implied CONTINUATIONs)
PP: PUSH_PROMISE frame (with implied CONTINUATIONs)
ES: END_STREAM flag
R: RST_STREAM frame
3.2 流的標志符
無符號的31位整數
- 客戶端發起的流具有奇數ID,服務器發起的流具有偶數ID。
- 0x0用來標識連接控制信息流且絕對不能用來建立一個新流。
- HTTP/1.1升級到HTTP/2的請求將收到一個0x1標識的流的響應
- 流標識符不能被重復使用,耗盡后發送一個GOAWAY幀強制客戶端對新流使用新連接(TCP連接)
3.3 流的並發
- SETTINGS_MAX_CONCURRENT_STREAMS:限制流的並發量(SETTINGS幀)
- 客戶端能指定服務端能啟動的流最大並發量,服務端能指定客戶端能啟動的流最大並發量
- 計入並發量:“打開”、任意一種“半封閉”狀態的流
4 流的優先級
4.1 概述
新建流的終端可以在報頭幀中包含優先級信息來對流標記優先級;對於已存在的流,優先級幀可來改變流優先級。
4.2 流依賴
- 每個流可以顯式依賴其他流;不依賴任何流的流的流依賴為0x0(虛擬根流)。
- 共有相同父節點的依賴流之間順序是不固定的。
- 專用標志導致流成為其父節點流唯一的子節點流,其他依賴流變成依賴此優先流。
4.3 依賴權重
- 所有有依賴的流以[1,256]的整數來標識權重。
- 具有相同父節點的流應該根據權重比例來分配資源。
5 發送應用數據(DATA幀)
- 應用數據可以分為多個DATA 幀,最后一幀要翻轉幀首部的END_STREAM 字段。
- 可是,為減少隊首阻塞,HTTP 2.0 標准要求DATA 幀不能超過2^14-1(16383)字節。長度超過這個閥值的數據,就得分幀發送。
參考:
- http2詳解
- HTTP/2 服務器推送(Server Push)教程
- 《Web性能權威指南》
- RFC 7540