netty系列之:netty實現http2中的流控制

flydean發表於2021-10-29

簡介

HTTP2相對於http1.1來說一個重要的提升就是流控制flowcontrol。為什麼會有流控制呢?這是因為不管是哪種協議,客戶端和伺服器端在接收資料的時候都有一個緩衝區來臨時儲存暫時處理不了的資料,但是緩衝區的大小是有限制的,所以有可能會出現緩衝區溢位的情況,比如客戶端向伺服器端上傳一個大的圖片,就有可能導致伺服器端的緩衝區溢位,從而導致一些額外的資料包丟失。

為了避免緩衝區溢位,各個HTTP協議都提供了一定的解決辦法。

在HTTP1.1中,流量的控制依賴的是底層TCP協議,在客戶端和伺服器端建立連線的時候,會使用系統預設的設定來建立緩衝區。在資料進行通訊的時候,會告訴對方它的接收視窗的大小,這個接收視窗就是緩衝區中剩餘的可用空間。如果接收視窗大小為零,則說明接收方緩衝區已滿,則傳送方將不再傳送資料,直到客戶端清除其內部緩衝區,然後請求恢復資料傳輸。

HTTP2通過客戶端和伺服器端的應用中進行緩衝區大小訊息的傳輸,通過在應用層層面控制資料流,所以各個應用端可以自行控制流量的大小,從而實現更高的連線效率。

本文將會介紹netty對http2流控制的支援。

http2中的流控制

在簡介中我們也提到了,傳統的HTTP1.1使用的是系統底層的流量控制機制,具體來說就是TCP的流控制。但是TCP的流控制在HTTP2中就不夠用了。因為HTTP2使用的是多路複用的機制,一個TCP連線可以有多個http2連線。所以對http2來說TCP本身的流控制機制太粗糙了,不夠精細。

所以在HTTP2中,實現了更加精細的流控制機制,它允許客戶端和伺服器實現其自己的資料流和連線級流控制。

具體的流程是這樣的,當客戶端和伺服器端建立連線之後,會傳送Http2SettingsFrame,這個settings frame中包含了SETTINGS_INITIAL_WINDOW_SIZE,這個是傳送端的視窗大小,用於 Stream 級別流控。流控制視窗的預設值設為65,535位元組,但是接收方可以對其進行修改,最大值為2^31-1 位元組。

建立好初始windows size之後,對於接收方來說,每次傳送方傳送data frame就會減少window的的大小,而接收方每次傳送WINDOW_UPDATE frame時候就會增加window的大小,從達到動態控制的目的。

netty對http2流控制的封裝

Http2FlowController

從上面的介紹我們知道,http2對流控制是通過兩個方面來實施的,第一個方面就是初始化的Http2SettingsFrame,通過設定SETTINGS_INITIAL_WINDOW_SIZE來控制初始window的大小。第二個方面就是在後續的WINDOW_UPDATE frame中對window的大小進行動態增減。

對於netty來說,這一切都是封裝在Http2FlowController類中的。Http2FlowController是一個抽象類,它有兩個實現,分別是Http2LocalFlowController和Http2RemoteFlowController。他們分別表示對inbound flow of DATA 和 outbound flow of DA他的處理。

Http2FlowController中主要有5個方法,分別是:

  • set channelHandlerContext:繫結flowcontrol到ChannelHandlerContext上。
  • set initialWindowSize:初始化window size,等同於設定SETTINGS_INITIAL_WINDOW_SIZE。
  • get initialWindowSize: 返回初始化window size。
  • windowSize: 獲取當前的windowSize。
  • incrementWindowSize: 增加flow control window的大小。

接下來我們看下他的兩個實現類,有什麼不一樣的地方。

Http2LocalFlowController

LocalFlowController用來對遠端節點發過來的DATA frames做flow control。它有5個主要的方法。

  • set frameWriter: 用來設定傳送WINDOW_UPDATE frames的frame writer。
  • receiveFlowControlledFrame: 接收inbound DATA frame,並且對其進行flow control。
  • consumeBytes: 表示應用已經消費了一定數目的bytes,可以接受更多從遠端節點發過來的資料。flow control可以傳送 WINDOW_UPDATE frame來重置window大小。
  • unconsumedBytes: 接收到,但是未消費的bytes。
  • initialWindowSize: 給定stream的初始window大小。

Http2RemoteFlowController

remoteFlowController用來處理髮送給遠端節點的outbound DATA frames。它提供了8個方法:

  • get channelHandlerContext: 獲取當前flow control的context.
  • addFlowControlled: 將flow control payload新增到傳送到遠端節點的queue中。
  • hasFlowControlled: 判斷當前stream是否有 FlowControlled frames在queue中。
  • writePendingBytes: 將流量控制器中的所有待處理資料寫入流量控制限制。
  • listener: 給 flow-controller新增listener。
  • isWritable: 確定流是否有剩餘位元組可用於流控制視窗。
  • channelWritabilityChanged: context的writable狀態是否變化。
  • updateDependencyTree: 更新stream之間的依賴關係,因為stream是可以有父子結構的。

流控制的使用

flowControl相關的類主要被用在Http2Connection,Http2ConnectionDecoder,Http2ConnectionEncoder中,在建立http2連線的時候起到相應的作用。

總結

flowControl是http2中的一個比較底層的概念,大家在深入瞭解netty的http2實現中應該會遇到。

本文已收錄於 http://www.flydean.com/29-netty-flowcontrol/

最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!

歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!

相關文章