一 Netty服務端NioEventLoop的啓動
Netty服務端建立、初始化完成後,再向Selector上註冊時,會將服務端Channel與NioEventLoop綁定,綁定以後,一方面會將服務端Channel的註冊工做當作Runnable任務提交到NioEventLoop的taskQueue,另外一方面,會開始NioEventLoop的啓動工做。從服務端Channel註冊Selector代碼的入口一直跟蹤,到以下代碼時,開始進行以上所說的操做。
以上代碼中eventLoop.inEventLoop()方法會判斷當前線程是否是在NioEventLoop中運行,初次運行在此,NioEventLoop中的thread變量尚未被賦值,因此返回false,執行eventLoop.execute方法,以下代碼所示,其中addTask方法將服務端Channel的註冊工做當作Runnable任務加入到taskQueue隊列。一樣,inEventLoop變量爲false,後面便開始執行啓動NioEventLoop的代碼startThread方法。
代碼運行在這裏後,NioEventLoop就啓動完畢,開始執行其run方法,在它的run方法中,NioEventLoop會作以下三件事:
①輪詢Channel中準備就緒的IO事件
②處理準備就緒的IO事件
③處理在任務隊列中的非IO任務,包括定時任務
下面主要分析一下服務端對新鏈接接入的處理。
二 Netty服務端接入新鏈接處理
從上圖所示的processSelectedKeys()方法進入日後跟一下,會處處理每個key的processSelectedKey()方法,進入該方法後首先會判斷key的有效性,而後便根據key所關聯的Channel已經準備好的事件進行分類處理,下圖紅框中的代碼就是服務端處理新鏈接接入的代碼,unsafe.read()方法會進入NioMessageUnsafe類的read方法,其中NioMessageUnsafe類是AbstractNioMessageChannel類的內部類。
由以上代碼可知,服務端處理新鏈接接入時先accept新鏈接,doReadMessages中只作了很簡單的操做,以下代碼所示,首先accept返回一個Java Nio底層SocketChannel,而後封裝爲Netty的NioSocketChannel放入List中,NioSocketChannel在建立過程當中,也和建立服務端Channel相似會爲客戶端Channel建立一系列Netty核心組件,好比Pipeline、unsafe等。
接下來就是就是真正的處理新接入的服務端Channel了,有一點先要明確,此時服務端Channel的pipeline爲:
head → ServerBootstrapAcceptor → tail,當執行pipeline.fireChannelRead(NioSocketChannel)方法時,事件沿着pipeline上的handler進行傳播,到ServerBootstrapAcceptor 的channelRead方法中開始處理新接入的客戶端Channel。在分析對客戶端Channel的處理以前,先看看在用戶代碼中配置ServerBootstrap的一個簡單示例:
ServerBootstrapAcceptor 的channelRead方法:
在channelRead方法中,首先將上游handler傳來的msg強轉爲Channel,而後配置客戶端Channel的pipeline,在pipeline中添加的childHandler就是在用戶代碼中配置ServerBootstrap時childerHander方法中傳入的ChannelInitializer這個handler,在後面會由ChannelInitializer的initChannel方法構造出真正處理數據的客戶端Channel的pipeline。此時,客戶端Channel的pipeline爲:head → ChannelInitializer → tail。設置完客戶端Channel的option與attr後,將會進行客戶端Channel的註冊工做,這一流程和註冊服務端Channel基本一致,區別是服務端Channel註冊過程當中是與boosGroup中的線程綁定,而客戶端Channel是與workGroup中的線程綁定。childGroup.register方法的childGroup就是用戶配置ServerBootstrap是傳入的workGroup。
childGroup.register方法跟進去後如上所示,next()會返回workGroup中的一個線程,後面的註冊過程就和服務端Channel的註冊如出一轍了。
至此,Netty服務端接收了一個客戶端鏈接,還爲客戶端Channel綁定了一個workGroup中的線程,而且完成了客戶端Channel的註冊及啓動客戶端Channel所綁定NioEventLoop。