天天看点

Netty EventLoop 和 EventLoopGroupNetty的线程模型NioEventLoop的源码分析

Netty的线程模型

1.1主从的 Reactor多线程模型

服务端用于接受客户端的连接不再是一个单独的NIO线程,而是一个独立的NIO线程池,Accertor接收到客户端的TCP连接请求之后,将新创建的SocketChannel注册到 I/O 线程池 netty使用的是这种。

Netty EventLoop 和 EventLoopGroupNetty的线程模型NioEventLoop的源码分析

服务端启动的时候,创建了两个NioEventLoop,它们是两个独立的Reactor线程池,一个用于接受TCP的连接,另一个用于处理I/O相关的读写操作,或者执行系统的Task,定时任务Task。

用于接受客户端连接的线程池职责是:

  • 接受客户端的连接,初始化Channel参数
  • 将链路状态变更事件 通知给ChannelPipline

用于处理I/O操作的线程池职责如下:

  • 异步读取通信对端的数据包,发送读事件到ChannelPipline;
  • 异步发送消息到通信对端,调用ChannelPipline的消息发送接口;
  • 执行系统的Task;
  • 执行定时的Task,例如链路空闲检测定时任务;

1.2 基于该线程模型的最佳实践

  • 创建两个NioEventLoopGroup 用于隔离Nio Acceptor和Nio I/O 线程;
  • 尽量不要再ChannelHandler 中启动用户线程(解码后用于将POJO 消息派发到后端服务端除外);
  • 解码要放在Nio的解码Handler 中进行,不要切换到用户线程中解码;
  • 如果业务逻辑非常简单,没有导致线程阻塞的磁盘操作,数据库操作,网络操作,可以在NIO线程完成业务处理;
  • 如果业务逻辑比较复杂,不要再NIO线程完成,建议将解码后的pojo消息封装为task,派发到业务线程池中有业务线程处理,保证Nio线程尽快被释放,处理其他的I/O操作。

1.2.1 推荐的线程数量计算公式

  • 线程数量 = (线程总时间 / 瓶颈资源时间)* 瓶颈资源的线程并行数

NioEventLoop的源码分析

2.1 NioEventLoop的设计原理

NioEventLoop 并不是一个纯粹的I/O线程 ,除了负责I/O操作,还兼顾处理一下任务:

  • 系统Task:通过调用execute(runable)方法实现,Netty有很多的系统task,创建他们的主要原因是:当I/O操作线程和用户线程同时操作网络资源时,为了防止并发导致锁竞争,将用户的线程的操作封装为task 放入消息对列中,让I/O线程去处理,这样实现了局部无锁。
  • 定时任务:调用schedule(runable,delay,timeunit)来实现。

继承类图

Netty EventLoop 和 EventLoopGroupNetty的线程模型NioEventLoop的源码分析

NioEventLoop

2.2run方法
Netty EventLoop 和 EventLoopGroupNetty的线程模型NioEventLoop的源码分析

Netty EventLoop 和 EventLoopGroupNetty的线程模型NioEventLoop的源码分析
Netty EventLoop 和 EventLoopGroupNetty的线程模型NioEventLoop的源码分析

2.3 出现bug的原因

服务端等待连接,客户端发起连接,发送消息,服务端接受连接,并注册监听通道的OP_READ,服务端读取消息,从感兴趣事件集合中移除OP_READ,客户端关闭连接,服务端给客户端发送消息,服务端select方法不再阻塞,无限唤醒并且返回值为0.

2.4 轮循到就绪状态SocketChannel 处理I/O

Netty EventLoop 和 EventLoopGroupNetty的线程模型NioEventLoop的源码分析
Netty EventLoop 和 EventLoopGroupNetty的线程模型NioEventLoop的源码分析

processSelectedKey 方法分析

Netty EventLoop 和 EventLoopGroupNetty的线程模型NioEventLoop的源码分析