簡介
無論是什麼協議,如果要真正被使用的話,需要將該協議轉換成為對應的語言才好真正的進行應用,本文將從http2訊息的結構出發,探討一下netty對http2訊息的封裝,帶大家領略一下真正的框架應該做到什麼程度。
http2訊息的結構
http2和http1.1不同的是它使用了新的二進位制分幀,通過客戶端和伺服器端建立資料流steam來進行客戶端和伺服器端之間訊息的互動。其中資料流是一個雙向位元組流,用來傳送一條或者多條訊息。
訊息是客戶端和服務端傳送的一個邏輯上完整的資料。根據資料大小的不同,可以將訊息劃分為不同的幀Frame。也就是說message是由不同的frame組成的。
frame就是http2中進行通訊的最小單位,根據上一節的介紹,我們知道frame有這樣幾種:
- DATA frame
- HEADERS frame
- PRIORITY frame
- RST_STREAM frame
- SETTINGS acknowledgment frame
- SETTINGS frame
- PING frame
- PING acknowledgment
- PUSH_PROMISE frame
- GO_AWAY frame
- WINDOW_UPDATE frame
- Unknown Frame
我們看一下http2中stream和frame的一個大體的結構:
在http2中,一個TCP連線,可以承載多個資料流stream,多個stream中的不同frame可以交錯傳送。
每個frame通過stream id來標記其所屬的stream。
有了上面的http2的基本概念,我們接下來就看下netty對http2的封裝了。
netty對http2的封裝
Http2Stream
作為一個TCP連線下面的最大的單位stream,netty中提供了介面Http2Stream。注意,Http2Stream是一個介面,它有兩個實現類,分別是DefaultStream和ConnectionStream。
Http2Stream中有兩個非常重要的屬性,分別是id和state。
id前面已經介紹了,是stream的唯一標記。這裡要注意由客戶端建立的 Stream ID 必須是奇數,而由服務端建立的 Stream ID 必須是偶數。另外Stream ID 為 0 的流有特殊的作用,它是CONNECTION_STREAM_ID,1 表示HTTP_UPGRADE_STREAM_ID。
state表示stream的狀態,具體而言,stream有下面幾個狀態:
IDLE(false, false),
RESERVED_LOCAL(false, false),
RESERVED_REMOTE(false, false),
OPEN(true, true),
HALF_CLOSED_LOCAL(false, true),
HALF_CLOSED_REMOTE(true, false),
CLOSED(false, false);
為什麼狀態需要區分local和remote呢?這是因為stream連線的兩端,所以有兩端的狀態。
和stream狀態相對應的就是http2的生命週期了。netty提供了Http2LifecycleManager來表示對http2生命週期的管理:
void closeStreamLocal(Http2Stream stream, ChannelFuture future);
void closeStreamRemote(Http2Stream stream, ChannelFuture future);
void closeStream(Http2Stream stream, ChannelFuture future);
ChannelFuture resetStream(ChannelHandlerContext ctx, int streamId, long errorCode,
ChannelPromise promise);
ChannelFuture goAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
ByteBuf debugData, ChannelPromise promise);
void onError(ChannelHandlerContext ctx, boolean outbound, Throwable cause);
分別是關閉stream,重置stream,拒絕新建stream:goAway,和處理出錯狀態這幾種。
Http2Frame
stream之後,就是真實承載http2訊息的Http2Frame了。在netty中,Http2Frame是一個介面,它有很多具體的實現。
Http2Frame的直接子類包括HTTP2GoAwayFrame、HTTPPingFrame、Http2SettingsFrame和HTTP2SettingsAckFrame。
其中goAway表示不接受新的stream,ping用來進行心跳檢測。SETTINGS用來修改連線或者 Stream 流的配置。
netty中專門有一個Http2Settings類和其對應。
在這個類中定義了一些特別的setting名字:
SETTINGS 名字 | 含義 |
---|---|
SETTINGS_HEADER_TABLE_SIZE | 對端索引表的最大尺寸 |
SETTINGS_ENABLE_PUSH | 是否啟用伺服器推送功能 |
SETTINGS_MAX_CONCURRENT_STREAMS | 接收端允許的最大併發 Stream 數量 |
SETTINGS_INITIAL_WINDOW_SIZE | 傳送端的視窗大小,用於 Stream 級別流控 |
SETTINGS_MAX_FRAME_SIZE | 設定幀的最大大小 |
SETTINGS_MAX_HEADER_LIST_SIZE | 對端頭部索引表的最大尺寸 |
除了上面講的4個frame之外,其他的frame實現都繼承自Http2StreamFrame,具體而言有PriorityFrame,ResetFrame,HeadersFrame,DataFrame,WindowUpdateFrame,PushPromiseFrame和UnknownFrame。
各個frame分別代表了不同的功能。這裡最重要的就是Http2HeadersFrame和Http2DataFrame。
Http2HeadersFrame主要是客戶端傳送給伺服器端的http2請求。
具體而言除了標準的http1.1的header之外,http2還支援下面的header:
METHOD(":method", true),
SCHEME(":scheme", true),
AUTHORITY(":authority", true),
PATH(":path", true),
STATUS(":status", false),
PROTOCOL(":protocol", true);
對於Http2DataFrame來說,他本身是一個ByteBufHolder,用來傳遞具體的資料資訊。data frame的Payload直接儲存在ByteBuf中。
總結
以上就是netty對http2訊息的封裝了。
本文的例子可以參考:learn-netty4
本文已收錄於 http://www.flydean.com/28-netty-wrap-http2/
最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!
歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!