Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析

一、Netty源码分析

https://github.com/netty/netty/tree/4.1/example/src/main/java/io/netty/example/echo

上找到echo这个example

1.1 服务端启动流程

1.1.1 两个NioEventLoopGroup

服务端代码首先要定义两个NioEventLoopGroup,打个断点进去

image-20220325200803748

NioEventLoopGroup继承MultithreadEventLoopGroup,加载的时候执行父类静态方法,初始化DEFAULT_EVENT_LOOP_THREADS = cpu核数 * 2

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第1张图片

如果是无参构造就让默认children线程为12个,而bossgroup是有参,只有1个线程,workergroup就是12个线程。这些线程就是事件循环组

image-20220325201358502

这个过程中还会生成一个Selector多路复用器的Provider,供之后生成Selector使用,是由操作系统决定的,Windows下就是WindowsSelectorProvider

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第2张图片

这个位置就是生成children线程的地方,生成完之后为每个线程加上结束回调监听器

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第3张图片 Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第4张图片

1.1.2 ServerBootStrap

接下来在ServerBootStrap那打个断点

image-20220325203356933

进去看看,加载ServerBootstrap会先加载AbstractBootstrap,声明了一堆变量,实际上都没初始化,留给后面链式编程添加属性

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第5张图片

然后针对链式编程进行断点研究

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第6张图片

group(bossGroup,workerGroup):将bossgroup赋给group,workergroup赋给childgroup

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第7张图片

channel(NioServerSocketChannel.class):用反射创建一个ChannelFactory,为之后创建NioServerSockerChannel提供一个工厂

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第8张图片

option(ChannelOption.SO_BACKLOG, 100):添加一些TCP参数,可以看到是放进options这个LinkedHashMap中,供之后使用

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第9张图片

handler(new LoggingHandler(LogLevel.INFO)):设置handler这个属性为LoggingHandler,即bossgroup的handler为LoggingHandler

image-20220325210003172

childHandler(new ChannelInitializer(){...}):设置childHandler,即workergroup的handler

image-20220325210527658

总结: ServerBootstrap实际上就是一个辅助启动类或者说是个配置类,将一些初始化属性添加进ServerBootstrap,供之后使用

1.1.3 ChannelFuture f = b.bind(PORT)

实际上bind方法才是真正工作的地方

image-20220325224054506 image-20220325224041126

实际上会走doBind()方法,doBind方法里有两个核心方法:initAndRegister()doBind0()

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第10张图片

initAndRegister()方法就是先构建一个NioServerSocketChannel,然后用init()方法初始化channel,初始化完后register这个channel

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第11张图片

先看init(),在init这个位置才将开始ServerBootstrap添加的options和attributes添加到ServerSocketChannel上,

然后拿到pipeline,往pipeline添加之前的handler这里是LoggingHandler,ServerBootstrapAcceptor监听ACCEPT连接事件

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第12张图片

再来看register(channel):首先用next()executors(线程池组)中获取一个executor(线程池)来执行register方法

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第13张图片

查看在不在当前的事件循环组,是就直接在当前register0(),不是就让这个eventLoop自己执行

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第14张图片

继续看register0(),发现还有一个doRegister(),之后safeSetSuccess()就代表注册完成了,后面的pipeline.fireChannelRegistered()是把遍历我们加入到channelpipeline的handler查看这些handler有没有对ChannelRegistered事件有操作

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第15张图片

死循环获取selectionKey,javaChannel实际上是Nio的channelServerSocketChannel,然后就进入NIO的register()了,用NIO的一套进行注册了

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第16张图片

之后safeSetSuccess()就代表注册完成了,safeSetSuccess()追进去就是notifyListeners,通知监听器我准备好了

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第17张图片

继续执行,回到doBind()方法,接下来执行doBind0()

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第18张图片 Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第19张图片

之后回到bind()方法执行fireChannelActive(),通知pipeline链上的handler:Channel开始活动,查看是否有Handler有ChannelActive的方法

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第20张图片

channelActive不仅会让handler调用他们处理ChannelActive的方法,还会让channel开始读数据

image-20220326124114525

readIfIsAutoRead()调用到最后到beginRead()

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第21张图片

之后执行到beginRead()->doBeginRead()

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第22张图片

之前初始化的时候设置ops为0,现在设置为16,表示Selectior开始监听ACCEPT事件

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第23张图片

最后进入NioEventLoop的run方法,开始Select

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第24张图片

1.2 客户端启动流程

1.2.1 connect

Bootstrap和服务端类似,直接看connect方法

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第25张图片 Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第26张图片

doResolveAndConnect():核心initAndRegister()doResolveAndConnect0()

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第27张图片

先看initAndRegister():和ServerBootStrap一样都是调用父类AbstractBootstrap的initAndRegister

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第28张图片

init是交给子类来重写的,可以看到ServerBootstrap的init要做的事就比较少了

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第29张图片

init完成之后调用AbstractChannel的register()-->register0() ,和ServerBootstrap类似,设置成功回调,通知pipeline进行ChannelRegistered操作

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第30张图片

随后从doResolveAndConnect0执行到doConnect,使用channel.connect()连接Socket地址

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第31张图片

追进去,从pipeline获取ChannelHandlerContext,找到出站的HandlerContext,调用这些HandlerContext的invokeConnect方法

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第32张图片 Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第33张图片

链的最后调用unsafe.connect()

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第34张图片 Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第35张图片

进入doConnect:NIO方式连接远程地址

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第36张图片

然后就可以看到Server的控制台:连接成功了

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第37张图片

之后就是select了,select到了就processSelectedKeys

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第38张图片

1.3 Server和Client建立连接和Read的过程

1.3.1 Server端

开启一个Client,这个地方的select会调用两次,第一次是建立ACCEPT连接事件,第二次是READ读事件

image-20220326131536819

调用selector.select(),监控是否有事件发送

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第39张图片

select到了一个,selectCnt++,调用processSelectedKeys()

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第40张图片

判空,然后调用processSelectedKeysOptimized()

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第41张图片

processSelectedKeysOptimized()方法调用processSelectedKey

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第42张图片

根据SelectionKey是什么事件调用相关方法,此处是read和accept,都调用read

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第43张图片 Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第44张图片

pipeline.fireChannelRead(byteBuf)通知pipeline做ChannelRead操作,也就是这个时候进行的输出

pipeline.fireChannelReadComplete()通知pipeline做ChannelReadComplete工作

可以看到输出结果,从ACCEPT到READ两次ChannelRead和两次READ COMPLETE,就是因为ACCEPT和READ都是走的read()方法

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第45张图片

1.3.2 Client端

Client同样select,但是走CONNECT这个地方

连接事件:

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第46张图片

走到最后就是pipeline.fireChannelActive

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第47张图片

调用pipeline链,到自定义的Handler有channlActive事件,这时候发消息过期,就是Server端处理ChannelRead的过程了

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第48张图片

11.4 服务端客户端启动总结

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第49张图片

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析_第50张图片

你可能感兴趣的:(Netty,Netty)