1. 程式人生 > 其它 >HTTP2基礎教程-讀書筆記(四)

HTTP2基礎教程-讀書筆記(四)

記錄一下HTTP/2的底層原理,幫助理解協議實現細節。

連線

每個端點都需要傳送一個連線作為最終確認使用的協議,並建立http/2連線的初始設定。客戶端和伺服器各自發送不同的連線前導(preface)。

客戶端的連線前導以24位的序列開頭,下面是16進製表示:

0x505249202a20485454502f322e300d0a0d0a534d0d0a0d0a

也就是下面字串:

這個序列後面必須跟著 SETTINGS 幀,其有可能為空。

服務端的連線前導由一個可能為空的 SETTINGS 幀組成,它在 HTTP/2 的連線中必須是第一個幀。

一旦連線前導交換過之後,連線就認為已經建立。端點可利用它進行通訊。

建立連線之後,就可以交換幀。所有幀的格式如下:

前面9個位元組是固定的,代表整個幀的大小。

下面解釋一下各個欄位的含義:

名稱

長度

描述

Length

3位元組

幀負載的長度

Type

1位元組

當前幀型別

Flags

1位元組

具體幀型別的標識

R

1位

保留位,不要設定

Stream Identifier

31位

每個流的唯一ID

Frame Payload

長度可變

真實的幀內容

h2中有10種不同型別的幀,如下表:

名稱

ID

描述

DATA

0x0

傳輸流的核心內容

HEADERS

0x1

包含HTTP首部和可選的優先順序引數

PRIORITY

0x2

指示或更改流的優先順序和依賴

RST_STREAM

0x3

允許一端停止流(通常由於錯誤導致)

SETTINGS

0x4

協商連線級引數

PUSH_PROMISE

0x5

提示客戶端,伺服器要推送些東西

PING

0x6

測試連線可用性和往來時延(RTT)

GOWAY

0x7

告訴另一端,當前端已結束

WINDOW_UPDATE

0x8

協商一端將要接受多少位元組(使用者流量控制)

CONTINUATION

0x9

用以擴充套件HEADER資料塊

“流”是在http/2連線中客戶端和服務端之間交換的一個獨立的、雙向的幀序列。流包含很多重要的特性:

  • 一個http/2連線可以包含多個併發的開放流和多個流中交錯的幀
  • 流可由客戶端或服務端單方面建立、使用或分享
  • 流可由其中任何一個端點關閉
  • 流中幀的順序很重要,接收者以它們被接收的順序處理。特別是,HEADERS 和 DATA 幀從語義上來說非常重要
  • 流是由一個整數標識,流ID 是在端點初始化流時被分配的

訊息

HTTP訊息泛指HTTP請求或響應。流是用來傳輸一對請求/響應訊息的。一個訊息至少由 HEADERS 幀組成,並且可以另外包含 CONTINUATION 和 DATA幀,以及其他的 HEADERS 幀。

下面是普通GET請求:

下面展示POST請求:

流量控制

不同h1,h2提供客戶端調整傳輸速度的能力,伺服器也可以控制。 WINDOW_UPDATE 幀用來指示流量控制資訊。客戶端需要流量控制的理由:

  • 確保某個流不會阻塞其他流
  • 可用頻寬和記憶體比較有限

目前為止流量控制沒有提供開發控制。

優先順序

h2使用流的依賴關係來解決伺服器同時收到很多請求不知道如何處理的問題。客戶端明確地和服務端溝通需要的資源以及它們的順序。通過宣告依賴關係樹和樹裡的相對權重:

  • 依賴關係:為客戶端提供了一種能力,通過指明某些物件對另一些物件有依賴,告知伺服器這些物件應該優先傳輸
  • 權重讓客戶端告訴伺服器如何確定具有共同依賴關係的物件的優先順序

服務端推送

提升單個物件效能的最佳方式,就是在它被用到之前就放到瀏覽器的快取裡。服務端推送同時伴隨著一些安全問題。

推送物件

若伺服器決定推送一個物件,會構造一個PUSH_PROMISE幀:

  • PUSH_PROMISE幀首部中的流ID用來關聯相關聯的請求
  • PUSH_PROMISE幀的首部塊與客戶端請求推送物件時傳送的首部塊是相似的。
  • 被髮送的物件必須確保是可快取的
  • :metch首部的值必須確保安全
  • 理想情況下,PUSH_PROMISE幀應早於客戶端接受到可能承載著推送物件的DATA幀
  • PUSH_PROMISE 幀會有對應流的ID

客戶端設定的流從1開始,使用奇數,而服務端開啟的流使用偶數,從2開始。這種設計避免了客戶端和伺服器之間流ID衝突,也可以輕鬆判斷哪些物件是由服務端推送的。0是保留數字,用於連線級控制訊息,不能用於建立新的流。

客戶端使用RST_STREAM或PROTOCOL_ERROR(專門留給PUSH_PROMISE涉及的協議層面問題)來拒收。值得注意的是,伺服器可以在PUSH_PROMISE傳送後立即啟動推送流,因此拒收推送仍然無法避免推送大量資源,所以推送正確的資源時不夠的,還需要只推送正確的資源

PUSH_PROMISE 中指明所屬流的ID:

首部壓縮

現在網頁平均包含140個請求,這些請求之間通常幾乎沒有新的或不同的內容,造成很大浪費,急需壓縮方法。經過思考和討論提出了HPACK,它是一種表查詢壓縮方案,利用霍夫曼編碼獲得接近GZIP的壓縮率,同時能抵禦CRIME。

如上兩個請求,只有紅框中的不同其餘都是重複的。HPACK是為減少傳輸相同部分而設計出來的。簡化原理如下:

假設客戶端傳送如下請求首部:

Header1:foo
Header2:bar
Header3:bat

同時客戶端會建立一張表:

索引

首部名稱

62

Header1

foo

63

Header2

bar

64

Header3

bat

服務端讀取到請求首部,照樣會建立一張表。客戶端傳送下一個請求時,若首部相同,可直接傳送如下首部塊:

62 63 64

伺服器會查詢先前的表格,把數字還原成索引對應的完整首部。

HPCK實現比上面的複雜得多,提供如下線索更深理解:

  • 請求端和響應端各維護兩張表格,一個動態表,另一個是61個常見首部的鍵值組合而成。
  • 如何索引欄位:1.傳送索引編號和文字值;2.僅傳送文字值,不對他們進行索引;3.傳送索引的首部名,值用文字表示,但不進行索引處理;4.傳送索引過的首部名和值
  • 使用打包方案的證書壓縮,以實現極高的空間效率
  • 利用霍夫曼編碼表進一步壓縮字串
  1. HTTP2基礎教程-讀書筆記(一)
  2. HTTP2基礎教程-讀書筆記(二)
  3. HTTP2基礎教程-讀書筆記(三)