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