https://github.com/netty/netty/tree/4.1/example/src/main/java/io/netty/example/echo
上找到echo这个example
服务端代码首先要定义两个NioEventLoopGroup,打个断点进去
NioEventLoopGroup继承MultithreadEventLoopGroup,加载的时候执行父类静态方法,初始化DEFAULT_EVENT_LOOP_THREADS = cpu核数 * 2
如果是无参构造就让默认children线程为12个,而bossgroup是有参,只有1个线程,workergroup就是12个线程。这些线程就是事件循环组
这个过程中还会生成一个Selector多路复用器的Provider,供之后生成Selector使用,是由操作系统决定的,Windows下就是WindowsSelectorProvider
这个位置就是生成children线程的地方,生成完之后为每个线程加上结束回调监听器
接下来在ServerBootStrap那打个断点
进去看看,加载ServerBootstrap会先加载AbstractBootstrap,声明了一堆变量,实际上都没初始化,留给后面链式编程添加属性
然后针对链式编程进行断点研究
group(bossGroup,workerGroup)
:将bossgroup赋给group,workergroup赋给childgroup
channel(NioServerSocketChannel.class)
:用反射创建一个ChannelFactory,为之后创建NioServerSockerChannel提供一个工厂
option(ChannelOption.SO_BACKLOG, 100)
:添加一些TCP参数,可以看到是放进options这个LinkedHashMap中,供之后使用
handler(new LoggingHandler(LogLevel.INFO))
:设置handler这个属性为LoggingHandler,即bossgroup的handler为LoggingHandler
childHandler(new ChannelInitializer
:设置childHandler,即workergroup的handler
总结: ServerBootstrap实际上就是一个辅助启动类或者说是个配置类,将一些初始化属性添加进ServerBootstrap,供之后使用
实际上bind方法才是真正工作的地方
实际上会走doBind()
方法,doBind方法里有两个核心方法:initAndRegister()
,doBind0()
initAndRegister()
方法就是先构建一个NioServerSocketChannel,然后用init()
方法初始化channel,初始化完后register这个channel
先看init()
,在init这个位置才将开始ServerBootstrap添加的options和attributes添加到ServerSocketChannel
上,
然后拿到pipeline,往pipeline添加之前的handler
这里是LoggingHandler,ServerBootstrapAcceptor
监听ACCEPT连接事件
再来看register(channel)
:首先用next()
从executors(线程池组)
中获取一个executor(线程池)
来执行register方法
查看在不在当前的事件循环组,是就直接在当前register0()
,不是就让这个eventLoop自己执行
继续看register0()
,发现还有一个doRegister()
,之后safeSetSuccess()
就代表注册完成了,后面的pipeline.fireChannelRegistered()
是把遍历我们加入到channelpipeline的handler查看这些handler有没有对ChannelRegistered
事件有操作
死循环获取selectionKey,javaChannel实际上是Nio的channelServerSocketChannel
,然后就进入NIO的register()
了,用NIO的一套进行注册了
之后safeSetSuccess()
就代表注册完成了,safeSetSuccess()
追进去就是notifyListeners,通知监听器我准备好了
继续执行,回到doBind()
方法,接下来执行doBind0()
之后回到bind()
方法执行fireChannelActive()
,通知pipeline链上的handler:Channel开始活动,查看是否有Handler有ChannelActive的方法
channelActive
不仅会让handler调用他们处理ChannelActive的方法,还会让channel开始读数据
readIfIsAutoRead()
调用到最后到beginRead()
之后执行到beginRead()
->doBeginRead()
之前初始化的时候设置ops为0,现在设置为16,表示Selectior开始监听ACCEPT事件
最后进入NioEventLoop的run方法,开始Select
Bootstrap和服务端类似,直接看connect方法
doResolveAndConnect()
:核心initAndRegister()
和doResolveAndConnect0()
先看initAndRegister()
:和ServerBootStrap一样都是调用父类AbstractBootstrap的initAndRegister
init
是交给子类来重写的,可以看到ServerBootstrap的init要做的事就比较少了
init完成之后调用AbstractChannel的register()-->register0()
,和ServerBootstrap类似,设置成功回调,通知pipeline进行ChannelRegistered操作
随后从doResolveAndConnect0
执行到doConnect
,使用channel.connect()连接Socket地址
追进去,从pipeline获取ChannelHandlerContext,找到出站的HandlerContext
,调用这些HandlerContext的invokeConnect
方法
链的最后调用unsafe.connect()
进入doConnect:NIO方式连接远程地址
然后就可以看到Server的控制台:连接成功了
之后就是select了,select到了就processSelectedKeys
开启一个Client,这个地方的select会调用两次,第一次是建立ACCEPT连接事件,第二次是READ读事件
调用selector.select(),监控是否有事件发送
select到了一个,selectCnt++,调用processSelectedKeys()
判空,然后调用processSelectedKeysOptimized()
processSelectedKeysOptimized()
方法调用processSelectedKey
根据SelectionKey是什么事件调用相关方法,此处是read和accept,都调用read
pipeline.fireChannelRead(byteBuf)
通知pipeline做ChannelRead操作,也就是这个时候进行的输出
pipeline.fireChannelReadComplete()
通知pipeline做ChannelReadComplete工作
可以看到输出结果,从ACCEPT到READ两次ChannelRead和两次READ COMPLETE,就是因为ACCEPT和READ都是走的read()
方法
Client同样select,但是走CONNECT这个地方
连接事件:
走到最后就是pipeline.fireChannelActive
调用pipeline链,到自定义的Handler有channlActive事件,这时候发消息过期,就是Server端处理ChannelRead的过程了