Netty-鳥瞰

Netty-鳥瞰

  • Bootstrap:Netty應用從構建一個Bootstrap開始,通過Bootstrap可以輕松的去配置并啟動應用。

  • ChannelHandler:為了能夠提供多協議并且多樣的去處理數據,Netty使用handler回調對象去處理特定的事件(包括正常的數據傳輸事件以及異常的處理事件)。通常我們可以實現ChannelInboundHandler,這樣我們可以把我們具體的業務邏輯處理封裝在這個我們實現的handler中。

  • ChannelInitializer:那我們怎么去綁定 ChannelHandler 去處理我們需要發送或者接收的消息呢?這里就用到ChannelInitializer,它的指責就是將 ChannelHandler 的實現加入到 ChannelPipeline。(事實上ChannelInitializer本身就是一個ChannelHandler,只不過這個handler會在加入其他handler的同時將自己從ChannelPipeline中移除)

  • ChannelPipeline: ChannelPipeline 和 EventLoop、EventLoopGroup相近都與事件和事件處理相關。

  • EventLoop & EventLoopGroup:指責在于處理通道中的IO操作,單個的 EventLoop 通常會處理多個通道上的事件。而 EventLoopGroup 包含了了多個 EventLoop ,并能用于去獲取 EventLoop。

  • Channel:一個通道代表了一個 socket 鏈接,或者能夠進行IO處理的組件,因此這里用EventLoop來管理。

  • ChannelFuture: Netty中的IO操作都是異步的(包括連接、讀、寫),這就意味著我們并不能知道操作是執行成功是否返回,但是我們需要在后續的操作中執行檢測或者注冊一些監聽器來獲取通知。Netty使用 Futures 和 ChannelFutures 去注冊監聽來獲取通知。

    ChannelFuture是一個特殊的 java.util.concurrent.Future,它允許我們注冊 ChannnelFutureListeners 到ChannelFuture。這些listener會在操作執行完成時得到通知。本質上來說,ChannelFuture是操作執行結果的占位符。所有的操作都會返回一個 ChannelFuture。


EventLoop

Netty 是一個非阻塞的,事件驅動的網絡框架。初看,Netty是用多線程來處理IO事件的。接觸過多線程編程的人可能會想,在這樣需要同步我們的代碼。但事實上,Netty的設計使我們不需要做過多的這些考慮。

EventLoop

如圖中所示,Netty使用 EventLoopGroup 的組件里面有一個或者多個 EventLoop。當一個通道(Channel)被注冊進來,Netty會綁定這個通道到一個單獨的 EventLoop (當然也是在一個單獨的線程中),并且這個通道的生命周期只會與這一個 EventLoop 綁定。這也就是為什么在我們的應用在Netty框架下不需要做同步處理(所有的IO操作都是在給定的通道及同一個線程中)

EventLoop 總是被綁定到一個單獨的線程中,在其生命周期中絕不會更換線程。

EventLoop

如圖:EventLoop 和 EventLoopGroup 是一種 "is-a"關系

一個 EventLoop 就是一個 EventLoopGroup,這也就意味著我們在傳入一個 EventLoopGroup 的地方同樣也能指定一個 EventLoop。

BootStrap & ServeBootStrap

BootStrap:用于創建客戶端;
ServerBootStrap:用于創建服務端;

不同點一:

ServerBootStrap 綁定到一個端口去監聽客戶端的鏈接;BootStrap 通常調用 connect() / bind(),然后在稍后使用 Channel (包含在ChannelFuture中)來進行連接。

不同點二:

客戶端 BootStrap 使用一個單獨的EventLoopGroup;然而,ServerBootStrap 使用兩個 EventLoopGroup (事實上使用同一個也是可以的),第一個集合包含一個單獨的 ServerChannel 代表服務端自己的socket(這個socket被綁定到本地的一個端口上了),第二個集合包含所有的服務端接收的鏈接通道。

Two EventLoopGroup

如圖,EventLoopGroupA 唯一的目的是接收鏈接然后將它們交付到 EventLoopGroupB。

Netty這樣做的根本目的是為了客服鏈接瓶頸。在一個高并發的場景下,可能會有極其多的鏈接接入,當只有一個Group時,處理已有鏈接已經很繁忙,以至于無法接收新的鏈接,這最終會導致很多鏈接會超時。而使用兩個Group,接收鏈接和處理鏈接分開,這樣所有的鏈接都可以被接收。

EventLoopGroup 可能包含多個EventLoop(不過也取決與我們的具體配置),每一個通道會有一個 EventLoop 與它綁定并且在整個生命周期內都不會更換。不過,由于 EventLoopGroup 中的 EventLoop 會比通道小,所以會有很多通道共享一個 EventLoop,這也意味著在同一個 EventLoop 中,一個通道處理繁忙的話,將不允許去處理其他的通道,因此不要使用阻塞EventLoop的原因。

One EvetLoopGroup

如圖,當只有一個group時,同一個實例會被使用兩次。

ChannelHandler

我們很容易想到 ChannelHandler 是用來處理數據流的,但是實際上 ChannelHandler 還能有很多其他的應用。

ChannelHandler

如圖,從類繼承關系上可以看出,我們有兩種 ChannelHandler,也反映出數據流是雙向的(數據可以從我們的應用向外流出,也能從遠端流入我們的應用)。

數據從一段流到另一端的過程中,會經過一個或者多個 ChannelHandler 的處理。這個 ChannelHandler 會被加入到應用中,并且它們加入的順序決定了它們處理數據的順序。

既然會設計到多個 ChannelHandler 協作,必然會有一定的規則需要遵守。這里的規則很簡單:ChannelPipeline 就是這寫 ChannelHandler 的約束。每一個 ChannelHandler 處理完自己的部分后都會將數據傳遞到同一個 ChannelPipeline 中的下一個 ChannelHandler,直到沒有 ChannelHandler 為止。

ChannelPipeline

如圖:反映了 ChannelInboundHandler 和 ChannelOutboundHandler 能夠同時存在于一個 ChannelPipeline 中。

由于我們的 ChannelHandler 通常實現自 ChannelInboundHandler 或 ChannelOutboundHandler 所以Netty會知道各個handler的類型,這樣在一個流出的事件中就可以跳過所有的 ChannelInboundHandler。

每一個加入 ChannelPipeline 中的 ChannelHandler 會得到一個 ChannelHandlerContext。通常獲得 ChannelHandlerContext 的引用是安全的,但是在 UDP 協議下可能不一定。 這個 ChannelHandlerContext 可以用于獲取底層的 channel 用于 write/send 消息。這樣就存在兩種方式來發送消息:直接寫到通道 或者 通過 ChannelHandlerContext 來寫消息,它們的主要區別是,直接寫到通道中的消息會從 ChannelPipeline 的尾部開始,寫到 ChannelHandlerContext 中的消息會傳遞給下一個handler

通過回調方法中攜帶的 ChannelHandlerContext 參數,我們可以將一個事件可以定向到下一個 ChannelInboundHandler 或者 前一個 ChannelOutboundHandler 中。(Netty為我們提供的抽象基類 ChannelInboundHandlerAdapter 和 ChannelOutboundHandlerAdapter 只提供單方向的傳遞,但是我們不需要手動調用傳遞方法)

Encoder & Decoder

每一個通道都有傳遞Netty事件的職責,Netty類中 *Adapter 結尾的類幫我們實現了這一過程,這樣我們不需要去關注這部分的工作,我們只需要去處理我們感興趣的部分。除了 *Adapter 的類外,同樣還有很多其他功能擴展的類我們可以使用,比如 encode/decode 消息。

當我們接收到消息時,我們必須將其從 bytes 轉化成 Java對象。當發送消息時,我們同樣需要將消息從Java對象轉換成bytes。這樣的操作很頻繁,因此Netty為我們提供了很多基礎類,類似于 ByteToMessageDecoder 和 MessageToByteEncoder 就提供這樣的功能。我們應用中用的最多的可能是讀取消息并解碼然后再進行一系列的其他處理,我們可以繼承 SimpleChannelInboundHandler<T> (T 就是我們要處理的消息類型),這個handler的主要方法channelRead0(ChannelHandlerContext,T),不能何時調用該方法,T 對象就是我們要處理的消息。

在IO線程中,不能進行阻塞的操作。Netty 允許在添加 ChannelHandler 到 ChannelPipeline 中時指定一個 EventExecutorGroup, 它會被用于獲取一個 EventExecutor 對象,這個 EventExecutor 將用于執行所有的ChannelHandler的操作(EventExecutor 會使用一個另外的線程)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,106評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,441評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,211評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,736評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,475評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,834評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,829評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,009評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,559評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,306評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,516評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,038評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,728評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,132評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,443評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,249評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,484評論 2 379

推薦閱讀更多精彩內容