Netty源码分析 I/O线程NioEventLoop
本文使用
netty-4.1.5.Final
版本源码进行分析
NioEvnetLoop作为Netty的I/O线程,主要负责轮询多路复用器,获取就绪的通道,执行网络的连接、客户端请求接入、读和写相关操作。EventLoop的职责不仅仅是处理网络I/O事件,用户自定义的Task和定时任务Task也统一由EventLoop负责处理,这样所有I/O操作都在I/O线程内部串行操作,从而实现了线程模型的统一,避免了多线程并发操作和锁竞争,提升了I/O线程的处理和调度性能。
NioEventLoopGroup
NioEvnetLoop通过NioEventLoopGroup创建,NioEventLoopGroup就是Netty的I/O线程池,首先看一下NioEventLoopGroup的继承关系:
创建Netty线程池NioEventLoopGroup
|
|
主要用来设置各种线程池参数,如线程数量,创建Thread的线程池,NIO的selectorProvider,拒绝策略等参数。
通过多层构造函数最终调用到父类MultithreadEventExecutorGroup构造函数中
|
|
|
|
该段主要是设置线程池相关参数,初始化netty线程池中的所有NioEventLoop线程,便于后续的注册监听。
NioEventLoop
NioEvnetLoop作为netty的I/O线程,内部绑定了一个通过线程池创建的Thread,该Thread在一个无线循环中监听处理I/O相关的操作。
Netty用于接收客户端请求的线程池职责如下。
接收客户端TCP连接,初始化Channel参数;
将链路状态变更事件通知给ChannelPipeline。
Netty处理I/O操作的Reactor线程池职责如下。
异步读取通信对端的数据报,发送读事件到ChannelPipeline;
异步发送消息到通信对端,调用ChannelPipeline的消息发送接口;
执行系统调用Task;
执行定时任务Task,例如链路空闲状态监测定时任务。
NioEventLoop的继承关系:
创建Netty线程NioEventLoop
|
|
通过创建netty线程池的相关参数,初始化NioEventLoop,在NioEventLoop上打开一个NIO的选择器Selector,用于处理注册在当前的NioEventLoop上的channel,每个NioEventLoop上的Selector相互独立互不影响。通过参数io.netty.noKeySetOptimization
可以设置是否开启selectedKeys优化策略,通过反射替换掉Selector内部原生的selectedKeys,替换为netty优化后的SelectedSelectionKeySet,默认开启。
NioEventLoop与线程绑定
|
|
当NioEventLoop执行时,会创建一个线程并将线程绑定在该NioEventLoop上,创建过程通过原子变量STATE_UPDATER做并发保护。该线程将会执行SingleThreadEventExecutor的run方法。
SingleThreadEventExecutor的run方法将在一个无线循环中处理I/O相关操作和task任务,直到退出。
|
|
处理netty的I/O操作,根据selectedKeys判断selectedKeys是否是netty优化过的selectedKeys来决定走不同的处理逻辑,遍历并执行就绪的selectKeys上的操作。
|
|
处理task任务和定时task任务
|
|
一般会将I/O相关的操作封装成task来执行,比如write操作,会将write操作封装成task,从而使用当前channel所在的I/O线程来处理该操作。这样所有I/O操作都在I/O线程内部串行操作,从而实现了线程模型的统一,避免了多线程并发操作和锁竞争,提升了I/O线程的处理和调度性能。表面上看,串行化设计似乎CPU利用率不高,并发程度不够。但是,通过调整netty线程池的数量,可以同时启动多个串行化的线程并行运行,这种局部无锁化的串行线程设计相比一个队列多个工作线程模型性能更优。