Netty-鳥瞰
Bootstrap:Netty應(yīng)用從構(gòu)建一個Bootstrap開始瞬浓,通過Bootstrap可以輕松的去配置并啟動應(yīng)用。
ChannelHandler:為了能夠提供多協(xié)議并且多樣的去處理數(shù)據(jù)磅叛,Netty使用handler回調(diào)對象去處理特定的事件(包括正常的數(shù)據(jù)傳輸事件以及異常的處理事件)。通常我們可以實現(xiàn)ChannelInboundHandler兆龙,這樣我們可以把我們具體的業(yè)務(wù)邏輯處理封裝在這個我們實現(xiàn)的handler中敲董。
ChannelInitializer:那我們怎么去綁定 ChannelHandler 去處理我們需要發(fā)送或者接收的消息呢紫皇?這里就用到ChannelInitializer,它的指責(zé)就是將 ChannelHandler 的實現(xiàn)加入到 ChannelPipeline坝橡。(事實上ChannelInitializer本身就是一個ChannelHandler精置,只不過這個handler會在加入其他handler的同時將自己從ChannelPipeline中移除)
ChannelPipeline: ChannelPipeline 和 EventLoop、EventLoopGroup相近都與事件和事件處理相關(guān)脂倦。
EventLoop & EventLoopGroup:指責(zé)在于處理通道中的IO操作,單個的 EventLoop 通常會處理多個通道上的事件赖阻。而 EventLoopGroup 包含了了多個 EventLoop ,并能用于去獲取 EventLoop棋电。
Channel:一個通道代表了一個 socket 鏈接,或者能夠進行IO處理的組件赶盔,因此這里用EventLoop來管理榆浓。
-
ChannelFuture: Netty中的IO操作都是異步的(包括連接、讀陡鹃、寫),這就意味著我們并不能知道操作是執(zhí)行成功是否返回闷叉,但是我們需要在后續(xù)的操作中執(zhí)行檢測或者注冊一些監(jiān)聽器來獲取通知。Netty使用 Futures 和 ChannelFutures 去注冊監(jiān)聽來獲取通知握侧。
ChannelFuture是一個特殊的 java.util.concurrent.Future,它允許我們注冊 ChannnelFutureListeners 到ChannelFuture状知。這些listener會在操作執(zhí)行完成時得到通知。本質(zhì)上來說饥悴,ChannelFuture是操作執(zhí)行結(jié)果的占位符盲再。所有的操作都會返回一個 ChannelFuture。
EventLoop
Netty 是一個非阻塞的贷揽,事件驅(qū)動的網(wǎng)絡(luò)框架梦碗。初看,Netty是用多線程來處理IO事件的印屁。接觸過多線程編程的人可能會想雄人,在這樣需要同步我們的代碼。但事實上础钠,Netty的設(shè)計使我們不需要做過多的這些考慮叉谜。
如圖中所示正罢,Netty使用 EventLoopGroup 的組件里面有一個或者多個 EventLoop翻具。當(dāng)一個通道(Channel)被注冊進來,Netty會綁定這個通道到一個單獨的 EventLoop (當(dāng)然也是在一個單獨的線程中)裆泳,并且這個通道的生命周期只會與這一個 EventLoop 綁定工禾。這也就是為什么在我們的應(yīng)用在Netty框架下不需要做同步處理(所有的IO操作都是在給定的通道及同一個線程中)
EventLoop 總是被綁定到一個單獨的線程中,在其生命周期中絕不會更換線程闻葵。
如圖:EventLoop 和 EventLoopGroup 是一種 "is-a"關(guān)系
一個 EventLoop 就是一個 EventLoopGroup槽畔,這也就意味著我們在傳入一個 EventLoopGroup 的地方同樣也能指定一個 EventLoop。
BootStrap & ServeBootStrap
BootStrap:用于創(chuàng)建客戶端鳞尔;
ServerBootStrap:用于創(chuàng)建服務(wù)端寥假;
不同點一:
ServerBootStrap 綁定到一個端口去監(jiān)聽客戶端的鏈接霞扬;BootStrap 通常調(diào)用 connect() / bind(),然后在稍后使用 Channel (包含在ChannelFuture中)來進行連接喻圃。
不同點二:
客戶端 BootStrap 使用一個單獨的EventLoopGroup级及;然而,ServerBootStrap 使用兩個 EventLoopGroup (事實上使用同一個也是可以的)怕吴,第一個集合包含一個單獨的 ServerChannel 代表服務(wù)端自己的socket(這個socket被綁定到本地的一個端口上了)县踢,第二個集合包含所有的服務(wù)端接收的鏈接通道。
如圖议经,EventLoopGroupA 唯一的目的是接收鏈接然后將它們交付到 EventLoopGroupB煞肾。
Netty這樣做的根本目的是為了客服鏈接瓶頸籍救。在一個高并發(fā)的場景下,可能會有極其多的鏈接接入蝙昙,當(dāng)只有一個Group時,處理已有鏈接已經(jīng)很繁忙败去,以至于無法接收新的鏈接烈拒,這最終會導(dǎo)致很多鏈接會超時。而使用兩個Group葫辐,接收鏈接和處理鏈接分開伴郁,這樣所有的鏈接都可以被接收。
EventLoopGroup 可能包含多個EventLoop(不過也取決與我們的具體配置)剂陡,每一個通道會有一個 EventLoop 與它綁定并且在整個生命周期內(nèi)都不會更換。不過鸭栖,由于 EventLoopGroup 中的 EventLoop 會比通道小晕鹊,所以會有很多通道共享一個 EventLoop,這也意味著在同一個 EventLoop 中溅话,一個通道處理繁忙的話飞几,將不允許去處理其他的通道屑墨,因此不要使用阻塞EventLoop的原因。
如圖卵史,當(dāng)只有一個group時程腹,同一個實例會被使用兩次寸潦。
ChannelHandler
我們很容易想到 ChannelHandler 是用來處理數(shù)據(jù)流的,但是實際上 ChannelHandler 還能有很多其他的應(yīng)用见转。
如圖斩箫,從類繼承關(guān)系上可以看出乘客,我們有兩種 ChannelHandler淀歇,也反映出數(shù)據(jù)流是雙向的(數(shù)據(jù)可以從我們的應(yīng)用向外流出,也能從遠端流入我們的應(yīng)用)牡直。
數(shù)據(jù)從一段流到另一端的過程中碰逸,會經(jīng)過一個或者多個 ChannelHandler 的處理饵史。這個 ChannelHandler 會被加入到應(yīng)用中,并且它們加入的順序決定了它們處理數(shù)據(jù)的順序约急。
既然會設(shè)計到多個 ChannelHandler 協(xié)作厌蔽,必然會有一定的規(guī)則需要遵守摔癣。這里的規(guī)則很簡單:ChannelPipeline 就是這寫 ChannelHandler 的約束。每一個 ChannelHandler 處理完自己的部分后都會將數(shù)據(jù)傳遞到同一個 ChannelPipeline 中的下一個 ChannelHandler戴卜,直到?jīng)]有 ChannelHandler 為止琢岩。
如圖:反映了 ChannelInboundHandler 和 ChannelOutboundHandler 能夠同時存在于一個 ChannelPipeline 中担孔。
由于我們的 ChannelHandler 通常實現(xiàn)自 ChannelInboundHandler 或 ChannelOutboundHandler 所以Netty會知道各個handler的類型吃警,這樣在一個流出的事件中就可以跳過所有的 ChannelInboundHandler酌心。
每一個加入 ChannelPipeline 中的 ChannelHandler 會得到一個 ChannelHandlerContext安券。通常獲得 ChannelHandlerContext 的引用是安全的侯勉,但是在 UDP 協(xié)議下可能不一定铝阐。 這個 ChannelHandlerContext 可以用于獲取底層的 channel 用于 write/send 消息。這樣就存在兩種方式來發(fā)送消息:直接寫到通道 或者 通過 ChannelHandlerContext 來寫消息饰迹,它們的主要區(qū)別是,直接寫到通道中的消息會從 ChannelPipeline 的尾部開始锹淌,寫到 ChannelHandlerContext 中的消息會傳遞給下一個handler
通過回調(diào)方法中攜帶的 ChannelHandlerContext 參數(shù)赠制,我們可以將一個事件可以定向到下一個 ChannelInboundHandler 或者 前一個 ChannelOutboundHandler 中钟些。(Netty為我們提供的抽象基類 ChannelInboundHandlerAdapter 和 ChannelOutboundHandlerAdapter 只提供單方向的傳遞政恍,但是我們不需要手動調(diào)用傳遞方法)
Encoder & Decoder
每一個通道都有傳遞Netty事件的職責(zé),Netty類中 *Adapter 結(jié)尾的類幫我們實現(xiàn)了這一過程迫筑,這樣我們不需要去關(guān)注這部分的工作宗弯,我們只需要去處理我們感興趣的部分脯燃。除了 *Adapter 的類外,同樣還有很多其他功能擴展的類我們可以使用蒙保,比如 encode/decode 消息辕棚。
當(dāng)我們接收到消息時,我們必須將其從 bytes 轉(zhuǎn)化成 Java對象。當(dāng)發(fā)送消息時逝嚎,我們同樣需要將消息從Java對象轉(zhuǎn)換成bytes扁瓢。這樣的操作很頻繁,因此Netty為我們提供了很多基礎(chǔ)類懈糯,類似于 ByteToMessageDecoder 和 MessageToByteEncoder 就提供這樣的功能涤妒。我們應(yīng)用中用的最多的可能是讀取消息并解碼然后再進行一系列的其他處理单雾,我們可以繼承 SimpleChannelInboundHandler<T> (T 就是我們要處理的消息類型)赚哗,這個handler的主要方法channelRead0(ChannelHandlerContext,T),不能何時調(diào)用該方法屿储,T 對象就是我們要處理的消息。
在IO線程中疯潭,不能進行阻塞的操作竖哩。Netty 允許在添加 ChannelHandler 到 ChannelPipeline 中時指定一個 EventExecutorGroup, 它會被用于獲取一個 EventExecutor 對象增淹,這個 EventExecutor 將用于執(zhí)行所有的ChannelHandler的操作(EventExecutor 會使用一個另外的線程)