Netty IO随记

Netty全部是异步IO操作,所有操作的是立即返回。可以通过future/监听器,等待/监听异步回调的结果。

注意:Netty的IO线程(boss+worker)最好只做不费时间的CPU操作,避免IO阻塞影响吞吐量。复杂业务请提交到自定义的业务线程池队列中,再由业务线程通过channel响应客户端请求。

线程模型

Netty Reactor 主从模型,介绍服务端 Netty 的工作架构图:

角色

  • 一个EventLoop有一个Thread线程对象,持有一个Selector(NIO多路复用器,调用系统内核select方法)。线程代码一直循环监听Selector。
  • 一个EventLoopGroup相当于一个EventLoop池。
  • 每一个Socket连接封装到一个SocketChannel中,是接收、发送信息的管道。
  • BossEventLoopGroup持有多个EventLoop,其Selector只负责监听Socket-Accetpt事件,将接入的Socket初始化SocketChannel,生成ChannelPipeline处理器链。最后注册到Work-Selector中。
  • WorkEventLoopGroup与Boss同理,但Selector只负责监听Socket-读写事件。(JavaNio模型中通过Selector多路复用器扫描指定事件Key监听触发事件的Channel)。
  • Socket读写事件经过SocketChannel的ChannelPipeline链中的In/OutChannelHandler入/出站处理器。
  • ChannelPipeline和ChannelHandler默认是Channel私有的。为ChannelHandler增加@Sharable注解使其变成单例。
  • TaskQueue、ScheduleTaskQueue用於单线程异步执行ChannelHandler中费时间的操作,避免EventLoop被阻塞。在ChannelHandler中执行ctx.channel().eventLoop().execute(new Runnable())可以将Runnable投递到其TaskQueue。

 

server流程图注意图中 NioEventGroup 是 NioEventLoop

1)初始化创建 2 个 NioEventLoopGroup:其中 boosGroup 用于 Accetpt 连接建立事件并分发请求,workerGroup 用于监听处理 I/O 读写事件和业务逻辑。

2)基于 ServerBootstrap(服务端启动引导类):配置 EventLoopGroup、Channel 类型,连接参数、配置入站、出站事件 handler。

3)绑定端口:开始工作。

Server 端包含 1 个 Boss NioEventLoopGroup 和 1 个 Worker NioEventLoopGroup。

NioEventLoopGroup 相当于 1 个事件循环组,这个组里包含多个事件循环 NioEventLoop,每个 NioEventLoop 包含 1 个 Selector 和 1 个事件循环线程。

每个 Boss NioEventLoop 循环执行的任务包含 3 步:

1)轮询 Accept 事件;

2)处理 Accept I/O 事件,与 Client 建立连接,生成 NioSocketChannel,并将 NioSocketChannel 注册到某个 Worker NioEventLoop 的 Selector 上;

3)处理任务队列runAllTasks中的任务。任务队列中的任务包括用户调用 eventloop.execute 或 schedule 执行的任务,或者其他线程提交到该 eventloop 的任务。

每个 Worker NioEventLoop 循环执行的任务包含 3 步:

1)轮询 Read、Write 事件;

2)处理 I/O 事件,即 Read、Write 事件,在 NioSocketChannel 可读、可写事件发生时进行处理;

3)处理任务队列中的任务,runAllTasks。

其中任务队列中的 Task 有 3 种典型使用场景:

① 用户程序自定义的普通任务:

ctx.channel().eventLoop().execute(newRunnable() {

   @Override

   publicvoidrun() {

       //...

   }

});

② 非当前 Reactor 线程调用 Channel 的各种方法:

例如在推送系统的业务线程里面,根据用户的标识,找到对应的 Channel 引用,然后调用 Write 类方法向该用户推送消息,就会进入到这种场景。最终的 Write 会提交到任务队列中后被异步消费。

③ 用户自定义定时任务:

ctx.channel().eventLoop().schedule(newRunnable() {

   @Override

   publicvoidrun() {

 

   }

}, 60, TimeUnit.SECONDS);

线程切换 

通过Queue实现Netty IO线程与业务线程的切换,最后的ChannelHandler将msg和channel等封装成Runnable送入业务线程池Queue。业务线程从Queue中拉取信息处理。业务线程通过channel向客户端发送信息,会调用ChannelPipeline的出站流程。

从上图可以看出,Outbound操作的主要处理流程如下:

  • I/O线程NioEventLoop从SocketChannel中读取数据报,将ByteBuf投递到ChannelPipeline,触发ChannelRead事件;
  • I/O线程NioEventLoop调用ChannelHandler链,直到将消息投递到自定义业务线程池中执行Runnable,然后I/O线程返回,继续后续的读写操作;
  • 业务线程调用ChannelHandlerContext.write(Object msg)方法进行消息发送;
  • 如果是由业务线程发起的写操作,ChannelHandlerInvoker将发送消息封装成Task,放入到I/O线程NioEventLoop的任务队列中,由NioEventLoop在循环中统一调度和执行。放入任务队列之后,业务线程返回;
  • I/O线程NioEventLoop调用ChannelHandler链,进行消息发送,处理Outbound事件,直到将消息放入发送队列,然后唤醒Selector,进而执行写操作。

 

心跳检测 IdleStateHandler

/p/10ee5629d580

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章