剖析Netty之底层原理(二)

lgli

共 1663字,需浏览 4分钟

 · 2020-11-17

逃离整个宇宙碰撞的意外

穿过黑暗尽头又通往哪里

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

是否能留住和你的记忆

星空不规则

无尽下坠


上次说到调用

io.netty.bootstrap.AbstractBootstrap#init

方法

由于是服务端,则实际调用

io.netty.bootstrap.ServerBootstrap#init

方法


b26dc35340e1386a1109a7e5743debfe.webp


132-133行:设置通道相关属性

135行:从通道中获取

io.netty.channel.ChannelPipeline

获取的ChannelPipeline是获取的啥呢?

这里是从一个

io.netty.channel.Channel

中获取的,这个Channel,是前面

0041c5f49a26afee1bdb73683d777f80.webp

中得到的,上文说到,这里是通过反射调用对应类的构造方法获取的,而这个对应类,是传入的

io.netty.channel.socket.nio.NioServerSocketChannel

那么这里的

io.netty.channel.Channel#pipeline


0c43823eebd76d45062a251ee0b76cfd.webp

实质是调用上图显示的

io.netty.channel.AbstractChannel#pipeline

方法,因为

io.netty.channel.socket.nio.NioServerSocketChannel

是继承于

io.netty.channel.AbstractChannel


a37fa124dc55f0cbde8ed87352885e64.webp


这里是直接返回AbstractChannel的一个属性值

io.netty.channel.DefaultChannelPipeline

那么这个属性值是什么时候赋值的呢?

剖析Netty之底层原理(一)中,有说到过,初始化的Channel时,是通过反射调用

io.netty.channel.socket.nio.NioServerSocketChannel

的无参构造,

这里再回顾下这个无参构造:

b49b91647c186140662b48dc1500fea9.webp


执行完

io.netty.channel.socket.nio.NioServerSocketChannel#newSocket

之后,得到一个

java.nio.channels.ServerSocketChannel对象,再调用

io.netty.channel.socket.nio.NioServerSocketChannel#NioServerSocketChannel(java.nio.channels.ServerSocketChannel)

方法

即:


a1e490fddb126f4a243edc43393ee111.webp

这里调用了父类方法,我们逐个点击进去

0ec84ad9ab7d49d80e5966640ccfaf0f.webp

777117060e29f7881b446fc30863a24b.webp


85ee555e48d4aa1d0d5d8d6372eeb950.webp


如上述过程所示,这里调用了

io.netty.channel.AbstractChannel#AbstractChannel(io.netty.channel.Channel)

方法

设置了属性值

io.netty.channel.DefaultChannelPipeline

d88a932c8d83ac1c717f7ff381c7a91f.webp

这里的

newChannelPipeline

方法,实质调用

io.netty.channel.AbstractChannel#newChannelPipeline

方法,


cd2b38e363cd13713dad6170b02a742f.webp


dafaa9df6a0fcad1f9dfcf026c3e7e50.webp



这里我们需要重点看这个类:

io.netty.channel.AbstractChannelHandlerContext

上图可以发现,设置了

io.netty.channel.AbstractChannelHandlerContext#next


io.netty.channel.AbstractChannelHandlerContext#prev

这里维护的两个

io.netty.channel.AbstractChannelHandlerContext

其结果构成一个双向链表

类似这样子的一个结果:

006bdb63519e452435a344baee9c80c9.webp


关于这个设计的目的,待后续使用到的时候再进一步了解,这里先了解到此


接着说上面的初始化方法

137-138行:将类属性值,赋予成员变量,这里需要指出的是,这里的

EventLoopGroup是前面我们设置的工作线程组,

ChannelHandler是前面我们设置的childHandler

即:


5c2321a9cfffa3a873292422848571e2.webp



140-143行:将Map, Object>

转化为Entry, Object>[]数组

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

0abea5cce764d44d962f3cfe663c1997.webp

即,设置的.option


e654ab3f50d7ad26094b76d3e45136b1.webp

 

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方法:截图显示下


f503e4c4ee2dfa62ec73395cede50e52.webp

这里主要分为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)


df90ee702415f60137a4f6ea1093795e.webp


接着执行

io.netty.util.concurrent.SingleThreadEventExecutor#execute(java.lang.Runnable, boolean)


f68ee787a9b15a119a67da92d8f20d52.webp


如上图所示,这里简单描述下这段代码,

首先添加前面的addLast方法添加到任务,然后判断当前事件是否轮询,如果没有轮询,则执行if方法体的方法

这里有个

io.netty.util.concurrent.SingleThreadEventExecutor#startThread

方法,

跟踪进去


2eb21ff460d8c1b194fd79005452ee51.webp


注意看947行,有个 

io.netty.util.concurrent.SingleThreadEventExecutor#doStartThread

方法,

跟踪进去

3cc9f066b7ee9bd712be05f4d59bb97e.webp


这里会通过线程操作执行run方法<上图989行代码>

然后这个this在这里指代的是什么呢?然后执行的run方法又是什么方法呢?

前面的分析不难看出,设置的事件轮询线程组是

io.netty.channel.nio.NioEventLoopGroup

所以这里可以猜到,执行的应该是

io.netty.channel.nio.NioEventLoop#run

方法

看下这几者之间的类关系图


a16e9b7af9856d1d8465dee2060cbdba.webp


关于几者初始化关系,会在后面用图表示出来,这里先不在详述

所以这里执行的是

io.netty.channel.nio.NioEventLoop#run

即:


763c944c388732afdd544ca938e48f88.webp


这里的一大段代码,后面会详细说到,目前仅仅做一个简单的介绍,此逻辑类似NIO中,通过轮询多路复用器中每个通道的不同状态,然后做不同的事,此逻辑类似的,根据不同的情况,完成不一样的逻辑《处理轮询key,以及数据读写等等操作》,这里的for循环,是个死循环,即程序运行期间则一直监听。


至此,初始化方法

io.netty.bootstrap.ServerBootstrap#init

结束


回到

io.netty.bootstrap.AbstractBootstrap#initAndRegister

继续

怕忘记了,这里截图显示下:


ac95a7a8a5fc16bbcb6bd25ed93417e5.webp


第311行结束

异常情况先不谈

看第323行:

c4726f99d14e71134f6ca3c426134cf2.webp

这里调用了一个config方法,然后调用group方法,在调用register方法,将前面的Channel注册进去,这里挨个详述源码:


config方法


b27f59081dc2d396946811ed708bdf9c.webp

由于这里是服务端,则调用

io.netty.bootstrap.ServerBootstrap#config

方法


c9e2a69e1d1fa01f916ec2ad1e913ca5.webp

这里直接返回一个config


f01c81090529e1a9b76e3ad0285599bf.webp


简单看下这个

io.netty.bootstrap.ServerBootstrapConfig


c02fc81011f349fd326c783979d83c6d.webp

这里是其简单的类关系图,这里new一个ServerBootstrapConfig的时候传入了一个this,简单看下,this到底做了什么?

追踪到底,这里是给父类

io.netty.bootstrap.AbstractBootstrapConfig

的bootstrap赋值


5cdb34919f74ef924b3e9e79739a5be5.webp



group方法

这里能理解到了,这个group方法,则是调用

io.netty.bootstrap.AbstractBootstrapConfig#group

方法


cd2d8cd3773d1bda7f417a0bcb40c6fb.webp

返回调用的是bootstrap.group()方法,这里即调用

ServerBootstrapConfig的group方法,然而,在

ServerBootstrapConfig类中,并没有找到对应方法,于是想当然的可以去父类中找

于是在其父类中找到

io.netty.bootstrap.AbstractBootstrap#group()

ec958ec7573b26997f8797691ddd7963.webp


这里是直接返回的一个group,


cb00f11253339e4d0d46f28d8842c08a.webp

这个

io.netty.channel.EventLoopGroup

则是前面设置的一个

io.netty.channel.nio.NioEventLoopGroup

即bossGroup


35a2b9ef7a84db34e9d8c035feba13a6.webp

因为在前面源码中,这个EventLoopGroup,才被赋值到

io.netty.bootstrap.AbstractBootstrapgroup属性


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

io.netty.channel.nio.NioEventLoopGroupregister方法

然后在NioEventLoopGroup中并没有找到这个方法,则去父类中找

最后发现是其父类

io.netty.channel.MultithreadEventLoopGroup#register(io.netty.channel.Channel)

中找到方法


33d38dcd9dc0f4d8f664fce195d05551.webp


首先这里调用next()方法,这个next()方法其实是调用父类的next()方法


io.netty.util.concurrent.MultithreadEventExecutorGroup#next


ff894dcc0de4d2d81177ec5040d4dd0a.webp

如上图所示,这里的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

实例

到此,初始注册方法就先告一段落


回到前面说到的方法:


cd7e81bfd625df3646cf28b3eeda7c91.webp

前面简单简述了红色标记的方法,总结起来就是构建pipeline,初始化相关参数信息

下面上图接着273行:

获取到前面的channel

下面的其他方法和if判断逻辑先略过,这里我们主要看281行和299行的doBind0方法

io.netty.bootstrap.AbstractBootstrap#doBind0

方法


68e8e739bc8b49eecc4e9a43700e24b2.webp


如上图所示,这里是直接将轮询通道中绑定的事件,执行线程方法

重点看356行:

af34b9ec7652ceeafc950bbb046cfa5b.webp


调用

io.netty.channel.AbstractChannel#bind(java.net.SocketAddress, io.netty.channel.ChannelPromise)

方法


a356872ec86e2e701524b611e8aed7b5.webp


继续调用

io.netty.channel.DefaultChannelPipeline#bind(java.net.SocketAddress, io.netty.channel.ChannelPromise)

方法


9f8241eb45420772c13464925e6cc5a5.webp


继续调用

io.netty.channel.AbstractChannelHandlerContext#bind(java.net.SocketAddress, io.netty.channel.ChannelPromise)

方法

778b3112168e10ca9a3d2178756bcb61.webp


这里通过层层调用,完成对端口的监听和其他

io.netty.channel.ChannelPromise

的操作


到此结束分享代码追踪层面的Netty服务端的运行逻辑,即什么时候轮询,什么时候执行响应Task。有一些乱,可以根据看源码挨个挨个来,后面会整理下思路和层级逻辑,画出类的时序图来帮助理解和消化,本期就先到这儿了,头有点大.......



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


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


浏览 14
点赞
评论
收藏
分享

手机扫一扫分享

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

手机扫一扫分享

举报