有一段事件沒(méi)有更新文章了,各種原因都有吧。搬家的瑣事,搬家后的安逸呵呵。不過(guò),OneCoder明白,絕不能放松。對(duì)于Netty的學(xué)習(xí),也該稍微深入一點(diǎn)了。
所以,這次
OneCoder花了幾天時(shí)間,仔細(xì)梳理了一下Netty的源碼,總結(jié)了一下ServerBootStrap的啟動(dòng)和任務(wù)處理流程,基本涵蓋了Netty的關(guān)鍵架構(gòu)。
該圖是
OneCoder通過(guò)閱讀Netty源碼,逐漸記錄下來(lái)的?;究梢哉f(shuō)明Netty服務(wù)的啟動(dòng)流程。這里在具體講解一下。
首先說(shuō)明,我們這次順利的流程是基于NioSocketServer的。也就是基于Java NIO Selector的實(shí)現(xiàn)方式。在第六講
《Java NIO框架Netty教程(六)-Java NIO Selector模式》中,我們已經(jīng)知道使用Selector的幾個(gè)關(guān)鍵點(diǎn),所以這里我們也重點(diǎn)關(guān)注一下,這些點(diǎn)在Netty中是如何使用的。
很多看過(guò)Netty源碼的人都說(shuō)Netty源碼寫的很漂亮??善猎谀哪??Netty通過(guò)一個(gè)ChannelFactory決定了你當(dāng)前使用的協(xié)議類型(Nio/oio等),比如,OneCoder這里使用的是NioServerSocket,那么需要聲明的Factory即為NioServerSocketChannelFactory,聲明了這個(gè)Factory,就決定了你使用的Channel,pipeline以及pipeline中,具體處理業(yè)務(wù)的sink的類型。這種使用方式十分簡(jiǎn)潔的,學(xué)習(xí)曲線很低,切換實(shí)現(xiàn)十分容易。
Netty采用的是基于事件的管道式架構(gòu)設(shè)計(jì),事件(Event)在管道(Pipeline)中流轉(zhuǎn),交由(通過(guò)pipelinesink)相應(yīng)的處理器(Handler)。這些關(guān)鍵元素類型的匹配都是由開(kāi)始聲明的ChannelFactory決定的。
Netty框架內(nèi)部的業(yè)務(wù)也遵循這個(gè)流程,Server端綁定端口的binder其實(shí)也是一個(gè)Handler,在構(gòu)造完Binder后,便要聲明一個(gè)Pipeline管道,并賦給新建一個(gè)Channel。Netty在newChannel的過(guò)程中,相應(yīng)調(diào)用了Java中的ServerSocketChannel.open方法,打開(kāi)一個(gè)channel。然后觸發(fā)fireChannelOpen事件。這個(gè)事件的接受是可以復(fù)寫的。Binder自身接收了這個(gè)事件。在事件的處理中,繼續(xù)向下完成具體的端口的綁定。對(duì)應(yīng)Selector中的 socketChannel.socket().bind()。然后觸發(fā)fireChannelBound事件。默認(rèn)情況下,該事件無(wú)人接受,繼續(xù)向下開(kāi)始構(gòu)造Boss線程池。我們知道在Netty中Boss線程池是用來(lái)接受和分發(fā)請(qǐng)求的核心線程池。所以在channel綁定后,必然要啟動(dòng)Boss線城池,隨時(shí)準(zhǔn)備接受client發(fā)來(lái)的請(qǐng)求。在Boss構(gòu)造函數(shù)中,第一次注冊(cè)了selector感興趣的事件類型,SelectionKey.OP_ACCEPT。至此,在第六講中介紹的使用Selector的幾個(gè)關(guān)鍵步驟都體現(xiàn)在Netty中了。在Boss中回啟動(dòng)一個(gè)死循環(huán)來(lái)查詢是否有感興趣的事件發(fā)生,對(duì)于第一次的客戶端的注冊(cè)事件,Boss會(huì)將Channel注冊(cè)給worker保存。
這里補(bǔ)充一下,也是圖中忽略的部分,就是關(guān)于worker線程池的初始化時(shí)機(jī)問(wèn)題。worker池的構(gòu)造,在最開(kāi)始構(gòu)造ChannelFactory的時(shí)候就已經(jīng)準(zhǔn)備好了。在NioServerSocketChannelFactory的構(gòu)造函數(shù)里,會(huì)new一個(gè)NioWorkerPool。在NioWorkerPool的基類AbstractNioWorkerPool的構(gòu)造函數(shù)中,會(huì)調(diào)用OpenSelector方法,其中也打開(kāi)了一個(gè)selector,并且啟動(dòng)了worker線程池。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | private void openSelector() {
try {
selector = Selector.open();
} catch (Throwable t) {
throw new ChannelException( "Failed to create a selector." , t);
}
boolean success = false ;
try {
DeadLockProofWorker.start(executor, new ThreadRenamingRunnable( this , "New I/O worker #" + id));
success = true ;
} finally {
if (!success) {
try {
selector.close();
} catch (Throwable t) {
logger.warn( "Failed to close a selector." , t);
}
selector = null ;
}
}
assert selector != null && selector.isOpen();
}
|
至此,會(huì)分線程啟動(dòng)AbstractNioWorker中run邏輯。同樣是循環(huán)處理任務(wù)隊(duì)列。
1 2 3 4 | processRegisterTaskQueue();
processEventQueue();
processWriteTaskQueue();
processSelectedKeys(selector.selectedKeys());
|
這樣,設(shè)計(jì)使事件的接收和處理模塊完全解耦。
由此可見(jiàn),如果你想從nio切換到oio,只需要構(gòu)造不同的ChannelFacotry即可。果然簡(jiǎn)潔優(yōu)雅。
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)
點(diǎn)擊舉報(bào)。