剖析Netty之底层原理(二)
逃离整个宇宙碰撞的意外
穿过黑暗尽头又通往哪里
时间也被吞没到了无人之际
是否能留住和你的记忆
星空不规则
无尽下坠
上次说到调用
io.netty.bootstrap.AbstractBootstrap#init
方法
由于是服务端,则实际调用
io.netty.bootstrap.ServerBootstrap#init
方法
132-133行:设置通道相关属性
135行:从通道中获取
io.netty.channel.ChannelPipeline
获取的ChannelPipeline是获取的啥呢?
这里是从一个
io.netty.channel.Channel
中获取的,这个Channel,是前面
中得到的,上文说到,这里是通过反射调用对应类的构造方法获取的,而这个对应类,是传入的
io.netty.channel.socket.nio.NioServerSocketChannel
那么这里的
io.netty.channel.Channel#pipeline
实质是调用上图显示的
io.netty.channel.AbstractChannel#pipeline
方法,因为
io.netty.channel.socket.nio.NioServerSocketChannel
是继承于
io.netty.channel.AbstractChannel
的
这里是直接返回AbstractChannel的一个属性值
io.netty.channel.DefaultChannelPipeline
那么这个属性值是什么时候赋值的呢?
在剖析Netty之底层原理(一)中,有说到过,初始化的Channel时,是通过反射调用
io.netty.channel.socket.nio.NioServerSocketChannel
的无参构造,
这里再回顾下这个无参构造:
执行完
io.netty.channel.socket.nio.NioServerSocketChannel#newSocket
之后,得到一个
java.nio.channels.ServerSocketChannel对象,再调用
io.netty.channel.socket.nio.NioServerSocketChannel#NioServerSocketChannel(java.nio.channels.ServerSocketChannel)
方法
即:
这里调用了父类方法,我们逐个点击进去
如上述过程所示,这里调用了
io.netty.channel.AbstractChannel#AbstractChannel(io.netty.channel.Channel)
方法
设置了属性值
io.netty.channel.DefaultChannelPipeline
这里的
newChannelPipeline
方法,实质调用
io.netty.channel.AbstractChannel#newChannelPipeline
方法,
这里我们需要重点看这个类:
io.netty.channel.AbstractChannelHandlerContext
上图可以发现,设置了
io.netty.channel.AbstractChannelHandlerContext#next
和
io.netty.channel.AbstractChannelHandlerContext#prev
这里维护的两个
io.netty.channel.AbstractChannelHandlerContext
其结果构成一个双向链表
类似这样子的一个结果:
关于这个设计的目的,待后续使用到的时候再进一步了解,这里先了解到此
接着说上面的初始化方法
137-138行:将类属性值,赋予成员变量,这里需要指出的是,这里的
EventLoopGroup是前面我们设置的工作线程组,
ChannelHandler是前面我们设置的childHandler
即:
140-143行:将Map
转化为Entry
这里的Map
即,设置的.option
145-162行:主要是设置这个ChannelPipeline
调用
io.netty.channel.ChannelPipeline#addLast(io.netty.channel.ChannelHandler...)
方法
传入
io.netty.channel.ChannelInitializer
对象,这里重写了
io.netty.channel.ChannelInitializer#initChannel(io.netty.channel.ChannelHandlerContext)
方法,
将自定义的Handler设置到pipeline中
这里先不深入到Pipeline中描述如何添加自定义的Handler了,后续将有详细的说明
这里我们需要重点看下这几个addLast方法:截图显示下
这里主要分为2个部分,一个是148行-152行,另一部分是154-160行
148-152行:
这里重写了
io.netty.channel.ChannelInitializer#initChannel
方法,传入的这个通道,很明显就是前面设置的
io.netty.channel.socket.nio.NioServerSocketChannel
从服务端socket通道中获取到pipeline
149行,读取配置的handler,前面并没有设置,所以这里是null
故而不会执行if方法,结束运行
154-160行:
154行,从
io.netty.channel.socket.nio.NioServerSocketChannel
获取事件轮询同时执行一个线程方法
先看这个
java.util.concurrent.Executor#execute
方法实际执行
io.netty.util.concurrent.SingleThreadEventExecutor#execute(java.lang.Runnable)
接着执行
io.netty.util.concurrent.SingleThreadEventExecutor#execute(java.lang.Runnable, boolean)
如上图所示,这里简单描述下这段代码,
首先添加前面的addLast方法添加到任务,然后判断当前事件是否轮询,如果没有轮询,则执行if方法体的方法
这里有个
io.netty.util.concurrent.SingleThreadEventExecutor#startThread
方法,
跟踪进去
注意看947行,有个
io.netty.util.concurrent.SingleThreadEventExecutor#doStartThread
方法,
跟踪进去
这里会通过线程操作执行run方法<上图989行代码>
然后这个this在这里指代的是什么呢?然后执行的run方法又是什么方法呢?
前面的分析不难看出,设置的事件轮询线程组是
io.netty.channel.nio.NioEventLoopGroup
所以这里可以猜到,执行的应该是
io.netty.channel.nio.NioEventLoop#run
方法
看下这几者之间的类关系图
关于几者初始化关系,会在后面用图表示出来,这里先不在详述
所以这里执行的是
io.netty.channel.nio.NioEventLoop#run
即:
这里的一大段代码,后面会详细说到,目前仅仅做一个简单的介绍,此逻辑类似NIO中,通过轮询多路复用器中每个通道的不同状态,然后做不同的事,此逻辑类似的,根据不同的情况,完成不一样的逻辑《处理轮询key,以及数据读写等等操作》,这里的for循环,是个死循环,即程序运行期间则一直监听。
至此,初始化方法
io.netty.bootstrap.ServerBootstrap#init
结束
回到
io.netty.bootstrap.AbstractBootstrap#initAndRegister
继续
怕忘记了,这里截图显示下:
第311行结束
异常情况先不谈
看第323行:
这里调用了一个config方法,然后调用group方法,在调用register方法,将前面的Channel注册进去,这里挨个详述源码:
config方法
由于这里是服务端,则调用
io.netty.bootstrap.ServerBootstrap#config
方法
这里直接返回一个config
简单看下这个
io.netty.bootstrap.ServerBootstrapConfig
这里是其简单的类关系图,这里new一个ServerBootstrapConfig的时候传入了一个this,简单看下,this到底做了什么?
追踪到底,这里是给父类
io.netty.bootstrap.AbstractBootstrapConfig
的bootstrap赋值
group方法
这里能理解到了,这个group方法,则是调用
io.netty.bootstrap.AbstractBootstrapConfig#group
方法
返回调用的是bootstrap.group()方法,这里即调用
ServerBootstrapConfig的group方法,然而,在
ServerBootstrapConfig类中,并没有找到对应方法,于是想当然的可以去父类中找
于是在其父类中找到
io.netty.bootstrap.AbstractBootstrap#group()
这里是直接返回的一个group,
即
这个
io.netty.channel.EventLoopGroup
则是前面设置的一个
io.netty.channel.nio.NioEventLoopGroup
即bossGroup
因为在前面源码中,这个EventLoopGroup,才被赋值到
io.netty.bootstrap.AbstractBootstrap的group属性
最后调用register方法,则实质调用的
io.netty.channel.nio.NioEventLoopGroup的register方法
然后在NioEventLoopGroup中并没有找到这个方法,则去父类中找
最后发现是其父类
io.netty.channel.MultithreadEventLoopGroup#register(io.netty.channel.Channel)
中找到方法
首先这里调用next()方法,这个next()方法其实是调用父类的next()方法
即:
io.netty.util.concurrent.MultithreadEventExecutorGroup#next
如上图所示,这里的chooser,是初始化阶段提到过的chooser,即2种初始化线程数量的方法,然后这里可能根据自身电脑配置不一样会有不一样的
io.netty.util.concurrent.EventExecutorChooserFactory.EventExecutorChooser
实例
这里可以发现next方法主要是调用下一个线程实例
结合注册方法,这里主要将当前的这个
io.netty.channel.Channel
注册到下一个线程池里线程实例中
这里先简单了解下,注册到线程实例的方法,调用的是
io.netty.channel.SingleThreadEventLoop#register(io.netty.channel.ChannelPromise)
方法,即单线程事件轮询器的注册方法
注册完成后,返回一个
io.netty.channel.ChannelFuture
实例
到此,初始注册方法就先告一段落
回到前面说到的方法:
前面简单简述了红色标记的方法,总结起来就是构建pipeline,初始化相关参数信息
下面上图接着273行:
获取到前面的channel
下面的其他方法和if判断逻辑先略过,这里我们主要看281行和299行的doBind0方法
io.netty.bootstrap.AbstractBootstrap#doBind0
方法
如上图所示,这里是直接将轮询通道中绑定的事件,执行线程方法
重点看356行:
调用
io.netty.channel.AbstractChannel#bind(java.net.SocketAddress, io.netty.channel.ChannelPromise)
方法
继续调用
io.netty.channel.DefaultChannelPipeline#bind(java.net.SocketAddress, io.netty.channel.ChannelPromise)
方法
继续调用
io.netty.channel.AbstractChannelHandlerContext#bind(java.net.SocketAddress, io.netty.channel.ChannelPromise)
方法
这里通过层层调用,完成对端口的监听和其他
io.netty.channel.ChannelPromise
的操作
到此结束分享代码追踪层面的Netty服务端的运行逻辑,即什么时候轮询,什么时候执行响应Task。有一些乱,可以根据看源码挨个挨个来,后面会整理下思路和层级逻辑,画出类的时序图来帮助理解和消化,本期就先到这儿了,头有点大.......
初学,请多指教,欢迎关注转发.......
有喜欢的欢迎转发,点击下面图片关注公众号,不定期更新干货!