服务端启动代码
|
|
启动过程分析
跟踪ServerBootstrap.bind()
方法,追到AbstractBootstrap.doBind()
:
|
|
跟踪initAndRegister()
|
|
这里先通过工厂创建了一个Channel
(这里服务端指定类型NioServerSocketChannel
),然后初始化这个Channel
。最后把这个Channel
注册到EventLoopGroup
上(config().group()
返回的是我们设置的Boss Group
)
创建channel
查看NioServerSocketChannel
构造方法
|
|
newSocket
使用SelectorProvider
的openServerSocketChannel
打开服务器套接字通道。SelectorProvider
主要工作是根据操作系统类型和版本选择合适的Provider
:如果Linux
内核版本>=2.6则,具体的SelectorProvider
为EPollSelectorProvider
,否则为默认的PollSelectorProvider
继续追溯super
方法到AbstractChannel
,如下方法:
|
|
这里主要做了2件事:
创建
NioMessageUnsafe
实例(客户端创建NioByteUnsafe
),该类为Channel
提供了用于完成网络通讯相关的底层操作,如connect(),read(),register(),bind(),close()
等;给该
Channel
创建DefaultChannelPipeline
并初始化,这里用的是默认Pipeline
12345678910111213protected DefaultChannelPipeline(Channel channel) {this.channel = ObjectUtil.checkNotNull(channel, "channel");succeededFuture = new SucceededChannelFuture(channel, null);voidPromise = new VoidChannelPromise(channel, true);// 创建tailtail = new TailContext(this);// 创建headhead = new HeadContext(this);head.next = tail;tail.prev = head;}具体的
ChannelPipeline
执行流程看前篇文章
到这里整个Channel
创建就结束了。
总结一下:
- 通过工厂创建一个
Channel
,这里的Channel
由于是在服务端,使用的是NioServerSocketChannel
, - 这个管道的构造方法中,会初始化一个原生的
ServerSocketChannel
,最后初始化pipeline
和unsafe
。 - 初始化
pipeline
的时候,又会初始化pipeline
中的Head
和Tail
。
初始化channel
服务端init()
实现方法在ServerBootstrap
中:
|
|
服务端的init()
分为2个步骤:
- 将
bootstrap
配置的属性设置到Channel
上面 - 给
NioServerSocketChannel
的pipeline
里,添加了一个ChannelHandle
-ChannelInitializer
,供注册后使用
初始化结束,此时NioServerSocketChannel
的pipeline
链表结构为:
|
|
注册channel
|
|
主要将创建并初始化后的Channel
注册到selector
上面:
- 将
Channel
注册到EventLoop
的selector
上; - 触发
Pipeline
上面ChannelHandler
的channelRegistered
这里的config().group()
结果是NioEventLoopGroup
,父类是MultithreadEventLoopGroup
|
|
这里的next()
方法最后执行到chooser.next()
,chooser
有两种:PowerOfTwoEventExecutorChooser
和GenericEventExecutorChooser
。如果该EventLoopGroup
中EventLoop
是2的倍数则选择PowerOfTwoEventExecutorChooser
(2的倍数方便位操作);因为这里的Group
是NioEventLoopGroup
,所以next()
返回的是NioEventLoop
继续看NioEventLoop.register()
,具体方法实现在SingleThreadEventLoop
中:
|
|
最终使用channel
中的unsafe()
注册,具体方法实现在AbstractUnsafe
中:
|
|
invokeHandlerAddedIfNeeded
: 注册成功后,找到初始化阶段通过pipeline.addLast()
加入的ChannelInitializer
,执行其ChannelInitializer
的initChannel
方法(添加ChannelHandle
-ServerBootstrapAcceptor
),之后将ChannelInitializer
在pipeline
中删除
注册结束,此时NioServerSocketChannel
的pipeline
链表结构为:
|
|
fireChannelRegistered
沿着pipeline
的head
到tail
,调用ChannelHandler.channelRegistered
方法
|
|
fireChannelActive
由于注册阶段和绑定bind
阶段都是异步的,如果此时注册完成时bind
阶段已经绑定了本地端口,会沿着pipeline
的head
到tail
,调用各个ChannelHandler
的channelActive
方法
bind
本地地址
将NioServerSocketChannel
内部ServerSocketChannel
绑定到本地的端口上面,然后调用fireChannelActive
通知pipeline
里的ChannelHandle
,执行其channelActive
方法。
|
|
AbstractBootstrap
的doBind0()
,内部会调用pipeline
中的bind
方法,逻辑为从tail
出发调用outbound
的ChannelHandler
的bind
方法。当前pipeline
链表结构中只有Head
是继承了outbound
接口
|
|
Head
的bind
方法由NioServerSocketChannel
创建过程中生成的unsafe
的实例NioMessageUnsafe
来执行,该实例的bind
方法继承于AbstractUnsafe.bind()
,然后触发fireChannelActive
|
|
这里的doBind(localAddress)
方法由创建的NioServerSocketChannel
执行
|
|
这里把java
原生的ServerSocketChannel
绑定到本地地址上
绑定到本地地址后会执行pipeline.fireChannelActive()
方法
|
|
这里的channelInactive
会调用HeadContext
类的channelInactive
方法:
|
|
这里首先执行fireChannelActive
方法,沿着pipeline
传播给后续ChannelInboundHandler
,执行hannelActive()
方法;接着执行readIfIsAutoRead()
,可以发现这个方法作用在pipeline
上,从tail
到head
遍历ChannelOutboundHandler
执行read()
方法,我们这里只有head
是outBound
,read()
方法最后进入AbstractNioChannel.doBeginRead()
|
|
可以发现在执行doRegister()
注册这个Channel
到selector
的时候,selectKey
赋予了0
|
|
这里readInterestOp
这个哪来的:在创建NioServerSocketChannel
的时候
|
|
在这里重新注册了accept
事件,也就是说从此开始,selector
再轮询到新的连接接入事件,就可以交给这个NioServerSocketChannel
来处理了
至此,Netty
服务器段已经启动,Channel
和ChannelPipeline
已经建立,EventLoop
也在不断的select()
查找准备好的I/O。
总结
- 通过工厂生成
NioServerSocketChannel
。 - 设置
NioServerSocketChannel
对accept
事件感兴趣。此时Channel
已经绑定了unsafe
和pipeline
- 初始化
Channel
,这里主要是设置Channel
的pipeline
中的handler
、attr
和option
。这里的handler
是特殊类型ChannelInitializer
,等待注册后回调。 - 注册到
EventLoopGroup
里,最终是注册到某一个NioEventLoop
上面 - 在注册时实际上的
register0
操作是异步的,register0
的主要作用是把原生Channel
注册到原生selector
上。 - 执行
doBind0
也是异步只想,这里其实是和register0
一起在执行的。doBind0
是把channel
注册到本地端口上 - 执行
pipeline.read()
方法,最终传递到AbstractNioChannel
,重新向selector
注册OP_ACCEPT
事件