剖析Netty之底層原理(二)
逃離整個(gè)宇宙碰撞的意外
穿過(guò)黑暗盡頭又通往哪里
時(shí)間也被吞沒(méi)到了無(wú)人之際
是否能留住和你的記憶
星空不規(guī)則
無(wú)盡下墜
上次說(shuō)到調(diào)用
io.netty.bootstrap.AbstractBootstrap#init
方法
由于是服務(wù)端,則實(shí)際調(diào)用
io.netty.bootstrap.ServerBootstrap#init
方法

132-133行:設(shè)置通道相關(guān)屬性
135行:從通道中獲取
io.netty.channel.ChannelPipeline
獲取的ChannelPipeline是獲取的啥呢?
這里是從一個(gè)
io.netty.channel.Channel
中獲取的,這個(gè)Channel,是前面

中得到的,上文說(shuō)到,這里是通過(guò)反射調(diào)用對(duì)應(yīng)類的構(gòu)造方法獲取的,而這個(gè)對(duì)應(yīng)類,是傳入的
io.netty.channel.socket.nio.NioServerSocketChannel
那么這里的
io.netty.channel.Channel#pipeline

實(shí)質(zhì)是調(diào)用上圖顯示的
io.netty.channel.AbstractChannel#pipeline
方法,因?yàn)?/span>
io.netty.channel.socket.nio.NioServerSocketChannel
是繼承于
io.netty.channel.AbstractChannel
的

這里是直接返回AbstractChannel的一個(gè)屬性值
io.netty.channel.DefaultChannelPipeline
那么這個(gè)屬性值是什么時(shí)候賦值的呢?
在剖析Netty之底層原理(一)中,有說(shuō)到過(guò),初始化的Channel時(shí),是通過(guò)反射調(diào)用
io.netty.channel.socket.nio.NioServerSocketChannel
的無(wú)參構(gòu)造,
這里再回顧下這個(gè)無(wú)參構(gòu)造:

執(zhí)行完
io.netty.channel.socket.nio.NioServerSocketChannel#newSocket
之后,得到一個(gè)
java.nio.channels.ServerSocketChannel對(duì)象,再調(diào)用
io.netty.channel.socket.nio.NioServerSocketChannel#NioServerSocketChannel(java.nio.channels.ServerSocketChannel)
方法
即:

這里調(diào)用了父類方法,我們逐個(gè)點(diǎn)擊進(jìn)去



如上述過(guò)程所示,這里調(diào)用了
io.netty.channel.AbstractChannel#AbstractChannel(io.netty.channel.Channel)
方法
設(shè)置了屬性值
io.netty.channel.DefaultChannelPipeline

這里的
newChannelPipeline方法,實(shí)質(zhì)調(diào)用
io.netty.channel.AbstractChannel#newChannelPipeline
方法,


這里我們需要重點(diǎn)看這個(gè)類:
io.netty.channel.AbstractChannelHandlerContext
上圖可以發(fā)現(xiàn),設(shè)置了
io.netty.channel.AbstractChannelHandlerContext#next
和
io.netty.channel.AbstractChannelHandlerContext#prev
這里維護(hù)的兩個(gè)
io.netty.channel.AbstractChannelHandlerContext
其結(jié)果構(gòu)成一個(gè)雙向鏈表
類似這樣子的一個(gè)結(jié)果:

關(guān)于這個(gè)設(shè)計(jì)的目的,待后續(xù)使用到的時(shí)候再進(jìn)一步了解,這里先了解到此
接著說(shuō)上面的初始化方法
137-138行:將類屬性值,賦予成員變量,這里需要指出的是,這里的
EventLoopGroup是前面我們?cè)O(shè)置的工作線程組,
ChannelHandler是前面我們?cè)O(shè)置的childHandler
即:

140-143行:將Map
轉(zhuǎn)化為Entry
這里的Map

即,設(shè)置的.option

?
145-162行:主要是設(shè)置這個(gè)ChannelPipeline
調(diào)用
io.netty.channel.ChannelPipeline#addLast(io.netty.channel.ChannelHandler...)
方法
傳入
io.netty.channel.ChannelInitializer
對(duì)象,這里重寫了
io.netty.channel.ChannelInitializer#initChannel(io.netty.channel.ChannelHandlerContext)
方法,
將自定義的Handler設(shè)置到pipeline中
這里先不深入到Pipeline中描述如何添加自定義的Handler了,后續(xù)將有詳細(xì)的說(shuō)明
這里我們需要重點(diǎn)看下這幾個(gè)addLast方法:截圖顯示下

這里主要分為2個(gè)部分,一個(gè)是148行-152行,另一部分是154-160行
148-152行:
這里重寫了
io.netty.channel.ChannelInitializer#initChannel
方法,傳入的這個(gè)通道,很明顯就是前面設(shè)置的
io.netty.channel.socket.nio.NioServerSocketChannel
從服務(wù)端socket通道中獲取到pipeline
149行,讀取配置的handler,前面并沒(méi)有設(shè)置,所以這里是null
故而不會(huì)執(zhí)行if方法,結(jié)束運(yùn)行
154-160行:
154行,從
io.netty.channel.socket.nio.NioServerSocketChannel
獲取事件輪詢同時(shí)執(zhí)行一個(gè)線程方法
先看這個(gè)
java.util.concurrent.Executor#execute
方法實(shí)際執(zhí)行
io.netty.util.concurrent.SingleThreadEventExecutor#execute(java.lang.Runnable)

接著執(zhí)行
io.netty.util.concurrent.SingleThreadEventExecutor#execute(java.lang.Runnable, boolean)

如上圖所示,這里簡(jiǎn)單描述下這段代碼,
首先添加前面的addLast方法添加到任務(wù),然后判斷當(dāng)前事件是否輪詢,如果沒(méi)有輪詢,則執(zhí)行if方法體的方法
這里有個(gè)
io.netty.util.concurrent.SingleThreadEventExecutor#startThread
方法,
跟蹤進(jìn)去

注意看947行,有個(gè)?
io.netty.util.concurrent.SingleThreadEventExecutor#doStartThread
方法,
跟蹤進(jìn)去

這里會(huì)通過(guò)線程操作執(zhí)行run方法<上圖989行代碼>
然后這個(gè)this在這里指代的是什么呢?然后執(zhí)行的run方法又是什么方法呢?
前面的分析不難看出,設(shè)置的事件輪詢線程組是
io.netty.channel.nio.NioEventLoopGroup
所以這里可以猜到,執(zhí)行的應(yīng)該是
io.netty.channel.nio.NioEventLoop#run
方法
看下這幾者之間的類關(guān)系圖

關(guān)于幾者初始化關(guān)系,會(huì)在后面用圖表示出來(lái),這里先不在詳述
所以這里執(zhí)行的是
io.netty.channel.nio.NioEventLoop#run
即:

這里的一大段代碼,后面會(huì)詳細(xì)說(shuō)到,目前僅僅做一個(gè)簡(jiǎn)單的介紹,此邏輯類似NIO中,通過(guò)輪詢多路復(fù)用器中每個(gè)通道的不同狀態(tài),然后做不同的事,此邏輯類似的,根據(jù)不同的情況,完成不一樣的邏輯《處理輪詢key,以及數(shù)據(jù)讀寫等等操作》,這里的for循環(huán),是個(gè)死循環(huán),即程序運(yùn)行期間則一直監(jiān)聽(tīng)。
至此,初始化方法
io.netty.bootstrap.ServerBootstrap#init
結(jié)束
回到
io.netty.bootstrap.AbstractBootstrap#initAndRegister
繼續(xù)
怕忘記了,這里截圖顯示下:

第311行結(jié)束
異常情況先不談
看第323行:

這里調(diào)用了一個(gè)config方法,然后調(diào)用group方法,在調(diào)用register方法,將前面的Channel注冊(cè)進(jìn)去,這里挨個(gè)詳述源碼:
config方法

由于這里是服務(wù)端,則調(diào)用
io.netty.bootstrap.ServerBootstrap#config
方法

這里直接返回一個(gè)config

簡(jiǎn)單看下這個(gè)
io.netty.bootstrap.ServerBootstrapConfig

這里是其簡(jiǎn)單的類關(guān)系圖,這里new一個(gè)ServerBootstrapConfig的時(shí)候傳入了一個(gè)this,簡(jiǎn)單看下,this到底做了什么?
追蹤到底,這里是給父類
io.netty.bootstrap.AbstractBootstrapConfig
的bootstrap賦值

group方法
這里能理解到了,這個(gè)group方法,則是調(diào)用
io.netty.bootstrap.AbstractBootstrapConfig#group
方法

返回調(diào)用的是bootstrap.group()方法,這里即調(diào)用
ServerBootstrapConfig的group方法,然而,在ServerBootstrapConfig類中,并沒(méi)有找到對(duì)應(yīng)方法,于是想當(dāng)然的可以去父類中找
于是在其父類中找到
io.netty.bootstrap.AbstractBootstrap#group()

這里是直接返回的一個(gè)group,
即

這個(gè)
io.netty.channel.EventLoopGroup
則是前面設(shè)置的一個(gè)
io.netty.channel.nio.NioEventLoopGroup
即bossGroup

因?yàn)樵谇懊嬖创a中,這個(gè)EventLoopGroup,才被賦值到
io.netty.bootstrap.AbstractBootstrap的group屬性
最后調(diào)用register方法,則實(shí)質(zhì)調(diào)用的
io.netty.channel.nio.NioEventLoopGroup的register方法
然后在NioEventLoopGroup中并沒(méi)有找到這個(gè)方法,則去父類中找
最后發(fā)現(xiàn)是其父類
io.netty.channel.MultithreadEventLoopGroup#register(io.netty.channel.Channel)
中找到方法

首先這里調(diào)用next()方法,這個(gè)next()方法其實(shí)是調(diào)用父類的next()方法
即:
io.netty.util.concurrent.MultithreadEventExecutorGroup#next

如上圖所示,這里的chooser,是初始化階段提到過(guò)的chooser,即2種初始化線程數(shù)量的方法,然后這里可能根據(jù)自身電腦配置不一樣會(huì)有不一樣的
io.netty.util.concurrent.EventExecutorChooserFactory.EventExecutorChooser
實(shí)例
這里可以發(fā)現(xiàn)next方法主要是調(diào)用下一個(gè)線程實(shí)例
結(jié)合注冊(cè)方法,這里主要將當(dāng)前的這個(gè)
io.netty.channel.Channel
注冊(cè)到下一個(gè)線程池里線程實(shí)例中
這里先簡(jiǎn)單了解下,注冊(cè)到線程實(shí)例的方法,調(diào)用的是
io.netty.channel.SingleThreadEventLoop#register(io.netty.channel.ChannelPromise)
方法,即單線程事件輪詢器的注冊(cè)方法
注冊(cè)完成后,返回一個(gè)
io.netty.channel.ChannelFuture
實(shí)例
到此,初始注冊(cè)方法就先告一段落
回到前面說(shuō)到的方法:

前面簡(jiǎn)單簡(jiǎn)述了紅色標(biāo)記的方法,總結(jié)起來(lái)就是構(gòu)建pipeline,初始化相關(guān)參數(shù)信息
下面上圖接著273行:
獲取到前面的channel
下面的其他方法和if判斷邏輯先略過(guò),這里我們主要看281行和299行的doBind0方法
io.netty.bootstrap.AbstractBootstrap#doBind0
方法

如上圖所示,這里是直接將輪詢通道中綁定的事件,執(zhí)行線程方法
重點(diǎn)看356行:

調(diào)用
io.netty.channel.AbstractChannel#bind(java.net.SocketAddress, io.netty.channel.ChannelPromise)
方法

繼續(xù)調(diào)用
io.netty.channel.DefaultChannelPipeline#bind(java.net.SocketAddress, io.netty.channel.ChannelPromise)
方法

繼續(xù)調(diào)用
io.netty.channel.AbstractChannelHandlerContext#bind(java.net.SocketAddress, io.netty.channel.ChannelPromise)
方法

這里通過(guò)層層調(diào)用,完成對(duì)端口的監(jiān)聽(tīng)和其他
io.netty.channel.ChannelPromise
的操作
到此結(jié)束分享代碼追蹤層面的Netty服務(wù)端的運(yùn)行邏輯,即什么時(shí)候輪詢,什么時(shí)候執(zhí)行響應(yīng)Task。有一些亂,可以根據(jù)看源碼挨個(gè)挨個(gè)來(lái),后面會(huì)整理下思路和層級(jí)邏輯,畫出類的時(shí)序圖來(lái)幫助理解和消化,本期就先到這兒了,頭有點(diǎn)大.......
初學(xué),請(qǐng)多指教,歡迎關(guān)注轉(zhuǎn)發(fā).......
有喜歡的歡迎轉(zhuǎn)發(fā),點(diǎn)擊下面圖片關(guān)注公眾號(hào),不定期更新干貨!
