深入淺出Netty:服務啟動

佔小狼發表於2016-09-09

本系列:

《深入淺出Netty(1)》


本節主要分析server的啟動過程。

Netty是基於Nio實現的,所以也離不開selector、serverSocketChannel、socketChannel和selectKey等,只不過Netty把這些實現都封裝在了底層。

從示例可以看出,一切從ServerBootstrap開始。

ServerBootstrap例項中需要兩個NioEventLoopGroup例項,分別為boss和work,有不同的分工:
1、 boss負責請求的accept操作。
2、 work負責請求的read、write和處理操作。

NioEventLoopGroup

NioEventLoopGroup主要負責管理eventLoop的生命週期,eventLoop數量預設為處理器個數的兩倍。

深入淺出Netty:服務啟動
NioEventLoopGroup

繼承關係如下:

深入淺出Netty:服務啟動
NioEventLoopGroup

NioEventLoopGroup構造方法:

MultithreadEventLoopGroup構造方法:

其中 DEFAULT_EVENT_LOOP_THREADS 為處理器數量的兩倍。

MultithreadEventExecutorGroup是核心,管理eventLoop的生命週期,先看看其中幾個變數。
1、children:EventExecutor陣列,儲存eventLoop。
2、chooser:從children中選取一個eventLoop的策略。

構造方法:

1、 根據陣列的大小,採用不同策略初始化chooser。
如果大小為2的冪次方,則採用PowerOfTwoEventExecutorChooser;否則使用GenericEventExecutorChooser。
判斷一個數是否是2的冪次方的方法,覺得很贊。

2、newChild方法過載,初始化EventExecutor時,實際執行的是NioEventLoopGroup中的newChild方法,所以,children元素的實際型別為NioEventLoop。


接下去看看NioEventLoop類。

NioEventLoop

每個eventLoop會維護一個selector和taskQueue,負責處理客戶端請求和內部任務,如ServerSocketChannel註冊和ServerSocket繫結等。

深入淺出Netty:服務啟動
NioEventLoop

繼承關係如下:

深入淺出Netty:服務啟動
NioEventLoop

構造方法:

當看到 selector = openSelector() 時,有沒有覺得親切了許多,這裡先不管 selector,看看SingleThreadEventLoop類。

SingleThreadEventLoop 構造方法:

啥事都沒做…


SingleThreadEventExecutor
從命名上可以看出,這是一個只有一個執行緒的執行緒池, 先看看其中的幾個變數:
1、state:執行緒池當前的狀態
2、taskQueue:存放任務的佇列
3、thread:執行緒池維護的唯一執行緒
4、scheduledTaskQueue:定義在其父類AbstractScheduledEventExecutor中,用以儲存延遲執行的任務。

構造方法:

程式碼很長,內容很簡單:
1、初始化一個執行緒,並線上程內部執行NioEventLoop類的run方法,當然這個執行緒不會立刻執行。
2、使用LinkedBlockingQueue類初始化taskQueue。

ServerBootstrap

通過serverBootstrap.bind(port)啟動服務,過程如下:

深入淺出Netty:服務啟動
bind過程

doBind實現如下

1、方法initAndRegister返回一個ChannelFuture例項regFuture,通過regFuture可以判斷initAndRegister執行結果。
2、如果regFuture.isDone()為true,說明initAndRegister已經執行完,則直接執行doBind0進行socket繫結。
3、否則regFuture新增一個ChannelFutureListener監聽,當initAndRegister執行完成時,呼叫operationComplete方法並執行doBind0進行socket繫結。

所以只有當initAndRegister操作結束之後才能進行bind操作。

initAndRegister

1、主要負責建立服務端channel,在本例子中,建立了NioServerSocketChannel。
2、為NioServerSocketChannel的pipeline新增handler。
3、註冊NioServerSocketChannel到selector。

NioServerSocketChannel

對Nio的ServerSocketChannel和SelectionKey進行了封裝。

繼承關係:

深入淺出Netty:服務啟動
NioServerSocketChannel繼承關係

構造方法:

1、方法newSocket利用 provider.openServerSocketChannel() 生成Nio中的ServerSocketChannel物件。
2、設定SelectionKey.OP_ACCEPT事件。

AbstractNioMessageChannel構造方法

啥也沒做…

AbstractNioChannel構造方法

設定當前ServerSocketChannel為非阻塞通道。

AbstractChannel構造方法

1、初始化unsafe。
這裡的Unsafe並非是jdk中底層Unsafe類,用來負責底層的connect、register、read和write等操作。

2、初始化pipeline。
每個Channel都有自己的pipeline,當有請求事件發生時,pipeline負責呼叫相應的hander進行處理。

unsafe和pipeline的具體實現原理會在後續進行分析。


回到ServerBootstrap的init(Channel channel)方法,新增handler到channel的pipeline中。

1、設定channel的options和attrs。
2、在pipeline中新增一個ChannelInitializer物件,每個client請求進來都會執行initChannel方法。


init執行完,需要把當前channel註冊到EventLoopGroup。

其實最終目的是為了實現Nio中把ServerSocket註冊到selector上,這樣就可以實現client請求的監聽了。

看看Netty中是如何實現的:

因為EventLoopGroup中維護了多個eventLoop,next方法會呼叫chooser策略找到下一個eventLoop,並執行eventLoop的register方法進行註冊。

channel.unsafe()是什麼?
NioServerSocketChannel初始化時,會建立一個NioMessageUnsafe例項,用於實現底層的register、read、write等操作。

1、register0方法提交到eventLoop執行緒池中執行,這個時候會啟動eventLoop中的執行緒。
2、方法doRegister()才是最終Nio中的註冊方法,方法javaChannel()獲取ServerSocketChannel。

ServerSocketChannel註冊完之後,通知pipeline執行fireChannelRegistered方法,pipeline中維護了handler連結串列,通過遍歷連結串列,執行InBound型別handler的channelRegistered方法,最終執行init中新增的ChannelInitializer handler。

1、initChannel方法最終把ServerBootstrapAcceptor新增到ServerSocketChannel的pipeline,負責accept客戶端請求。
2、在pipeline中刪除對應的handler。
3、觸發fireChannelRegistered方法,可以自定義handler的channelRegistered方法。

到目前為止,ServerSocketChannel完成了初始化並註冊到seletor上,啟動執行緒執行selector.select()方法準備接受客戶端請求。

細心的同學已經發現,ServerSocketChannel的socket還未繫結到指定埠,那麼這一塊Netty是如何實現的?
Netty把註冊操作放到eventLoop中執行。

最終由unsafe實現埠的bind操作。

bind完成後,且ServerSocketChannel也已經註冊完成,則觸發pipeline的fireChannelActive方法,所以在這裡可以自定義fireChannelActive方法,預設執行tail的fireChannelActive。

channel.read()方法會觸發pipeline的行為:

最終會在pipeline中找到handler執行read方法,預設是head。

至此為止,server已經啟動完成。

END。

打賞支援我寫出更多好文章,謝謝!

打賞作者

打賞支援我寫出更多好文章,謝謝!

深入淺出Netty:服務啟動

相關文章