Java NIO 图解 Netty 服务端启动的过程 | 京东云技术团队

一.启动概述
了解整体 Netty 常用的核心组件后,并且对比了传统 IO 模式。在对比过程中,找到了传统 IO 对应 Netty 中是如何实现的。最后我们了解到在 netty 中常用的那些组件。

本文在了解下这些核心组件的前提下,进一步了解组件如何在整个服务器启动过程如何被创建,如何组件之间配合来使用。首先也是先了解下大概服务端的启动过程,并且在了解过程中我们带着自己的问题去在学习过程中探寻答案。
1.1 启动概述

1.2 启动问题
netty 服务端启动是如何设置非阻塞模式的?
服务端启动后事件是如何注册到 selector 上?
二.启动详述
2.1 channel 创建
还是一样首先在 channel 创建过程大概有哪些过程
bind
initAndRegister
默认构造函数创建 channel
具体调用关系

时序图中从 1,2,3 步都好理解。
2.1.1 创建 channelFactory
从类的反射得到 channel 这里是一个关键点需要说明:

图中直接使用 channelFactory 来实现了 channel 的实例化.那么就按图索骥这个 channelFactory 是什么时候赋值的。

图中我们一步步找到 channelFactory 的路径。我们再看第三步是谁调用了
channel(Class<? extends C> channelClass) 创建了 channelFactory。最后发现我们在 bootstrap 设置 channel 属性的时候设置了 channelFactory。

走到这里我们才真正进入知道在 bootstrap 设置 NioServerSocketChannel。并利用 NioServerSocketChannel 创建。

上述步骤我们小结一下。通过设置 bootstrap 的 Channel 属性来设置服务端 NioServerSocketChannel 生成了 channelFactory。channelFactory 来主动调用传入的 class 来构造 channel。
2.2.2 NioServerSocketChannel 默认构造函数创建 Channel
从上图中我们知道该构造工厂是调用类的构造函数来进行初始化,通过代码中我们知道构造类为 NioServerSocketChannel。那么就看下 NioServerSocketChannel 构造过程。
先上来还是看下整体步骤:

通过默认的 newSocket 方法创建了 jdk 底层的 channel,然后通过 Channel 的配置了 channel 的 id,对应 channel 底层的读写 unsafe 组件,channel 对应的逻辑处理 pipeline,
最后通过 configureBlocking 设置 channel 为非阻塞模式【回答了我们第一个问题】。
第三步:通过 channelConfig 设置一些 tcp 层面的设置
至此 channel 创建完成
2.2 channel 初始化
通过上面 2.1 整个 channel 已经创建完成。第二大步在创建 channel 的基础上,给 channel 做一些属性配置。


上图设置用的属性 对应到代码中 为如下的代码

这里均为一些自己的属性和配置的设置,至此 channel 的创建和初始化完成。这里的配置用以客户端新建连接时进行设置。
2.3 selector 注册
还是先整体看下 整个 selector 流程,是如何将 channel 注册到 selector 上。

ps:以上代码流程需要一层层通过断点方式进行跟踪。
第一步:AbstractBootStrap register 入口

以上的步骤完成 channel 的创建和初始化,通过 initAndRegister 内部方法来实现 selector 挂载.
第二步:调用
io.netty.channel.MultithreadEventLoopGroup#register(io.netty.channel.Channel)
register 方法中通过 next 方法获取下一个可以调用 EventLoop 来调用它的 register 方法
第三步:调用
io.netty.channel.SingleThreadEventLoop#register(io.netty.channel.Channel)
调动 channel.unsafe.register 方法获取 channel 底层操作,并调用 unsafe 的 register 方法
第四步:通过调用 abstract Nio Channel 将 channel 注册到 eventLoop 的 selector 中【第二个问题的答案】,并将当前 channel 作为 attache 与 socket channel 关联。

完成对应的 selector 和 channel 绑定之后如果有相应的事件回调会进行事件回调操作,这里需要的话需要继承 ChannelInboundHandlerAdapter 中对应的方法 channelRegistered, channelActive。

至此 nio channel 通过 java 底层的 socketchannel 绑定 到指定的 selector ,完成 selector 与 niochannel 的注册过程。
2.4 端口绑定
基本步骤


第一步:底层端口 channel 绑定

第二步:最终调用 AbstractNioChannel doBeginRead 方法。
激活 channel 并向 selector 注册 read 事件。

三 .总结
整体了解到 netty 服务端的启动过程。
通过 NioServerSocketChannel 的 channelFactory 创建 channelFactory 方法
channelFactory 通过反射调用 nioServerSocketChannel 构造函数。创建 channel 对象
channel 通过 NioServerSocketChannelConfig 将自定义的 option,attr,handler 设置完成。
abstract Nio Channel 调用 register 方法实现 selector 和 channel 的绑定
最终调用 java channel 进行端口绑定并向 selector 注册 read 事件
整体 netty 服务端启动完成。
作者:京东科技 陈方林
来源:京东云开发者社区 转载请注明来源
版权声明: 本文为 InfoQ 作者【京东科技开发者】的原创文章。
原文链接:【http://xie.infoq.cn/article/b3c5423760cce97be59e5f6df】。文章转载请联系作者。
评论