剖析Netty之底层原理(二)

lgli

共 1748字,需浏览 4分钟

 ·

2020-11-20 03:02

逃离整个宇宙碰撞的意外

穿过黑暗尽头又通往哪里

时间也被吞没到了无人之际

是否能留住和你的记忆

星空不规则

无尽下坠


上次说到调用

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, Object>

转化为Entry, Object>[]数组

这里的Map, Object>,是在初始化ServerbootStrap中设置的

即,设置的.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.AbstractBootstrapgroup属性


最后调用register方法,则实质调用的

io.netty.channel.nio.NioEventLoopGroupregister方法

然后在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。有一些乱,可以根据看源码挨个挨个来,后面会整理下思路和层级逻辑,画出类的时序图来帮助理解和消化,本期就先到这儿了,头有点大.......



初学,请多指教,欢迎关注转发.......


有喜欢的欢迎转发,点击下面图片关注公众号,不定期更新干货!


浏览 60
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报