03 Netty的組件和設(shè)計(jì)

點(diǎn)擊查看 《Netty in Action》筆記目錄

本文是對《Netty in Action》第3章內(nèi)容的筆記和翻譯区拳,主要內(nèi)容包括:

  • Netty 技術(shù)和架構(gòu)方面的介紹
  • Channel浅蚪、EventLoopChannelFuture
  • ChannelHandlerChannelPipeline
  • 啟動( Bootstrapping)

Channel、EventLoop 和 ChannelFuture

下面這些可以被看做 Netty 網(wǎng)絡(luò)模型的抽象:

  • ChannelSockets
  • EventLoop:流控烫罩、多線程惜傲、并發(fā)
  • ChannelFuture:異步通知

Channel 接口

Java 中基本的網(wǎng)絡(luò) I/O 操作依賴 Socket 類,如:bind()贝攒、connect()盗誊、read()write()。Netty 中的 Channel 接口提供的 API 與直接操作 Sockets API 相比隘弊,大大減少了復(fù)雜度哈踱。此外,Channel 是很多專有實(shí)現(xiàn)的基類梨熙,這些實(shí)現(xiàn)包括:

  • EmbeddedChannel
  • LocalServerChannel
  • NioDatagramChannel
  • NioSctpChannel
  • NioSocketChannel

EventLoop 接口

EventLoop 定義了 Netty 核心抽象:在一個連接周期中處理發(fā)生的事件开镣。圖3.1 在一個高層次展示了 ChannelsEventLoops咽扇、ThreadsEventLoopGroups 的關(guān)系邪财。關(guān)系如下:

  • 一個 EventLoopGroup 包括了一個或多個 EventLoop陕壹。
  • 一個 EventLoop 在生命周期中只會綁定到一個單一的線程 Thread
  • 所有被 EventLoop 處理的 I/O 事件都會被它綁定的線程 Thread 處理树埠。
  • 一個 Channel 會在一個單一的 EventLoop 中注冊它的生命周期糠馆。
  • 一個單一的 EventLoop 可能會被分配多個 Channels
圖3.1 Channels怎憋、EventLoops 和 EventLoopGroups 的關(guān)系

值得注意的是:在這個設(shè)計(jì)中又碌,一個給定 Channel 的 I/O 的操作都是由同一個線程執(zhí)行的,也就是這些操作是單線程的绊袋,所以無形中消除了同步的問題毕匀。

ChannelFuture 接口

Netty 中提供了 ChannelFuture,這個接口具有 addListener()方法愤炸,通過這個方法可以注冊一個 ChannelFutureListener期揪,這個監(jiān)聽器會在方法結(jié)束時(shí)被調(diào)用(無論執(zhí)行是否成功)。

ChannelFuture 的更多介紹
可以把 ChannelFuture 看做是存放結(jié)果的一個站位空間规个,這里的結(jié)果將會在未來的某個時(shí)刻被填滿凤薛。可以確定的是:對于同一個 Channel 的所有操作都會被執(zhí)行诞仓,并且執(zhí)行順序與調(diào)用的順序一致缤苫。

ChannelHandler 和 ChannelPipeline

ChannelHandler 接口

對于應(yīng)用開發(fā)者來說,Netty 中最重要的組件是 ChannelHandler墅拭。這個組件承載了對輸入輸出數(shù)據(jù)的所有應(yīng)用處理邏輯活玲。

事實(shí)上,ChannelHandler 幾乎可以滿足所有的操作谍婉,包括:轉(zhuǎn)換數(shù)據(jù)的格式舒憾、處理過程中拋出的異常等。

ChannelPipeline 接口

ChannelPipeline 提供了鏈?zhǔn)?ChannelHandlers 的一個承載容器穗熬,并且定義了在這個鏈中傳遞輸入輸出事件的 API镀迂。當(dāng)一個 Channel 被創(chuàng)建的時(shí)候,它會被自動綁定到它所在的 ChannelPipeline 上唤蔗。

ChannelHandler 通過以下步驟安裝到 ChannelPipeline 上:

  • ServerBootstrap 上注冊一個 ChannelInitializer 的實(shí)現(xiàn)探遵。
  • 當(dāng) ChannelInitializer.initChannel() 函數(shù)被調(diào)用的時(shí)候,ChannelInitializer 將會在 pipeline 中安裝一系列的 ChannelHandler妓柜。
  • 然后 ChannelInitializerChannelPipeline 中把它自身刪除箱季。

圖3.2 展示了從 ChannelHandler 派生出來的 ChannelInboundHandlerChannelOutboundHandler

圖3.2 ChannelHandler 類的繼承關(guān)系

圖3.3 展示了 Netty 應(yīng)用中輸入和輸出端數(shù)據(jù)流的區(qū)別棍掐。

圖3.3 輸入和輸出端數(shù)據(jù)流的區(qū)別

輸入輸出處理器的更多介紹
一個事件可以通過 ChannelHandlerContext 傳遞到鏈中的下一個處理器藏雏,ChannelHandlerContext 在每個方法中都會被作為一個輸入?yún)?shù)。因?yàn)槟阌袝r(shí)候會忽略不感興趣的事件作煌,Netty 提供了抽象基礎(chǔ)類 ChannelInboundHandlerAdapterChannelOutboundHandlerAdapter诉稍。每個都提供了方法實(shí)現(xiàn)蝠嘉,通過調(diào)用 ChannelHandlerContext 中的相應(yīng)方法,可以簡化傳遞到下一個處理者的操作杯巨。然后蚤告,你可以擴(kuò)展類并覆蓋你感興趣的方法。

當(dāng)兩種類別處理器在同一個 ChannelPipeline 中混合時(shí)會發(fā)生什么服爷?盡管輸入輸出 handler 都繼承于 ChannelHandler杜恰,但 Netty 還是會區(qū)分 ChannelInboundHandlerChannelOutboundHandler 的不同實(shí)現(xiàn),并且 確保數(shù)據(jù)只在方向相同類型的 handler 之間傳遞仍源。

當(dāng) ChannelHandler 被加入到 ChannelPipeline 中時(shí)心褐,它會被分配一個 ChannelHandlerContext,這個 ChannelHandlerContext 代表了 ChannelHandlerChannelPipeline 之間的綁定關(guān)系笼踩。

Netty 中發(fā)送數(shù)據(jù)的方法有兩種:

  • 直接寫入到 Channel逗爹,消息會從 ChannelPipeline 的尾部開始傳遞。
  • 寫入到 ChannelHandler 關(guān)聯(lián)的 ChannelHandlerContext嚎于,消息會從 ChannelPipeline 中的__下一個__handler 開始傳遞掘而。

進(jìn)一步了解 ChannelHandler

Netty 中有一些適配類,從而減少了常用 ChannelHandler 類的工作量于购。這些適配類提供了相應(yīng)接口方法的默認(rèn)實(shí)現(xiàn)袍睡。

你在寫 handle 時(shí)經(jīng)常會用到的適配類包括:

  • ChannelHandlerAdapter
  • ChannelInboundHandlerAdapter
  • ChannelOutboundHandlerAdapter
  • ChannelDuplexHandlerAdapter

編碼和解碼

當(dāng)你使用 Netty 發(fā)送和接收數(shù)據(jù)時(shí),需要進(jìn)行數(shù)據(jù)格式的轉(zhuǎn)換肋僧。輸入的消息將會被解碼斑胜,也就是將輸入的字節(jié)轉(zhuǎn)換為另一種格式,典型的是轉(zhuǎn)化為一個 Java 對象嫌吠。如果數(shù)據(jù)是輸出的止潘,相反的操作將會發(fā)生:會從現(xiàn)在的格式編碼成自己。需要這樣做的原因很簡單辫诅,網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù)是一系列的字節(jié)凭戴。

所有 Netty 提供的編碼解碼(encoder/decoder)的適配類都實(shí)現(xiàn)于 ChannelInboundHandler 或者 ChannelOutboundHandler

你將發(fā)現(xiàn):對于輸入數(shù)據(jù)泥栖,channelRead 方法或事件會被覆寫簇宽。對于輸入 Channel 的每條數(shù)據(jù)勋篓,這個方法都會被調(diào)用吧享。然后會調(diào)用解碼器的 decode() 方法,并將解碼后的字節(jié)傳輸?shù)?pipeline 中的下一個 ChannelInboundHandler譬嚣。

而輸出數(shù)據(jù)的模式正好相反:編碼器將消息轉(zhuǎn)換為字節(jié)钢颂,并將這些字節(jié)傳遞到下一個 ChannelOutboundHandler

抽象類 SimpleChannelInboundHandler

很常見的是拜银,你的應(yīng)用需要實(shí)現(xiàn)一個 handler 來接受解碼的數(shù)據(jù)并且在數(shù)據(jù)上實(shí)現(xiàn)應(yīng)用邏輯殊鞭。創(chuàng)建這樣的一個 ChannelHandler遭垛,你只需要擴(kuò)展基類 SimpleChannelInboundHandler<T>,其中 T 是你需要處理的數(shù)據(jù)對應(yīng)的 Java 類型操灿。

這個 handler 中最重要的一個方法是 channelRead0(ChannelHandlerContext,T)锯仪。

Bootstrapping

Netty 的啟動類可以為應(yīng)用的網(wǎng)絡(luò)層提供配置容器。

面向連接的協(xié)議
請記字貉巍:嚴(yán)格意義上的連接(“connection”)只適用于面向連接的協(xié)議庶喜,如 TCP。在這樣的協(xié)議中救鲤,可以保證連接的端之間可以按序接收到數(shù)據(jù)久窟。

相應(yīng)的,有兩種類型的啟動類:一個是客戶端(Bootstrap)本缠,另一個是服務(wù)端(ServerBootstrap)斥扛。

下表對這兩種類型的啟動進(jìn)行了比較。

類別 Bootstrap ServerBootstrap
網(wǎng)絡(luò)功能 連接遠(yuǎn)程主機(jī)和端口 綁定本地端口
EventLoopGroup 的數(shù)目 1 2

啟動一個客戶端只需要一個 EventLoopGroup丹锹,但是服務(wù)端的 ServerBootstrap 需要兩個(可以是同一個實(shí)例)稀颁,這是為什么?

服務(wù)端需要兩個區(qū)分的 Channel 集合卷仑。第一個集合包括一個 ServerChannel峻村,它代表了服務(wù)端自己用于監(jiān)聽(listening)的 socket,綁定到本地端口锡凝;第二個集合包括了所有創(chuàng)建的 Channels粘昨,用于處理輸入服務(wù)端接收到的各個客戶端連接。圖3.4 展示了這個模型窜锯,并說明了為什么需要2個不同的 EventLoopGroups张肾。

圖3.4 服務(wù)端的2個 EventLoopGroup

ServerChannel 相關(guān)的 EventLoopGroupServerChannel分配了一個 EventLoop,這個 EventLoop 負(fù)責(zé)為即將到來的連接請求創(chuàng)建 Channel锚扎。一旦接收到連接吞瞪,第二個 EventLoopGroup 會為創(chuàng)建出來的 Channel 分配對應(yīng)的 EventLoop

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末驾孔,一起剝皮案震驚了整個濱河市芍秆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌翠勉,老刑警劉巖妖啥,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異对碌,居然都是意外死亡荆虱,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來怀读,“玉大人诉位,你說我怎么就攤上這事〔思希” “怎么了苍糠?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長啤誊。 經(jīng)常有香客問我椿息,道長,這世上最難降的妖魔是什么坷衍? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任寝优,我火速辦了婚禮,結(jié)果婚禮上枫耳,老公的妹妹穿的比我還像新娘乏矾。我一直安慰自己,他們只是感情好迁杨,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布钻心。 她就那樣靜靜地躺著,像睡著了一般铅协。 火紅的嫁衣襯著肌膚如雪捷沸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天狐史,我揣著相機(jī)與錄音痒给,去河邊找鬼。 笑死骏全,一個胖子當(dāng)著我的面吹牛苍柏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播姜贡,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼试吁,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了楼咳?” 一聲冷哼從身側(cè)響起熄捍,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎母怜,沒想到半個月后余耽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡糙申,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年宾添,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片柜裸。...
    茶點(diǎn)故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡缕陕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出疙挺,到底是詐尸還是另有隱情扛邑,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布铐然,位于F島的核電站蔬崩,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏搀暑。R本人自食惡果不足惜沥阳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望自点。 院中可真熱鬧桐罕,春花似錦、人聲如沸桂敛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽术唬。三九已至薪伏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間粗仓,已是汗流浹背嫁怀。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留借浊,地道東北人眶掌。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像巴碗,于是被迫代替她去往敵國和親朴爬。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內(nèi)容