本文主要梳理下Netty里的EventLoop。

EventLoop并非Netty所独有,它是一种事件等待和处理的程序模型,可以解决多线程资源消耗高的问题,EventLoop在node.js中也有使用。下图是EventLoop通用的运行模式。每当事件发生时,应用程序都会将产生的事件放入事件队列中,然后EventLoop会轮询从队列中取出事件执行或者将事件分发给相应的事件监听者执行。事件执行的方式通常分为立即执行、延后执行、定期执行几种。
image.png

下面将Netty里的EventLoop进行展开。

Netty里的EventLoop可以理解为Reactor线程模型的事件处理引擎,每EventLoop线程都维护一个Selector选择器和任务队列taskQueue,它主要负责处理IO事件、普通任务和定时任务。下面从事件处理和任务处理两个部分介绍Netty EventLoop的实现原理。

事件处理机制
Netty中推荐使用NioEventLoop作为实现类,NioEventLoop的事件处理机制采用的是无锁串行化的设计思路。

  1. BossEventLoopGroup和WorkerEventLoopGroup包含一个或多个NioEventLoop,BossEventLoopGroup负责监听客户端的Accept事件,当事件触发时,将事件注册到WorkerEventLoopGroup中的一个NioEventLoop上。每新建一个Channel,只选择一个NioEventLoop与其绑定。所以说Channel生命周期的所有事件处理都是线程独立的,不同的NioEventLoop线程之间不会发生任何交集。
  2. NioEventLoop完成数据读取后,会调用绑定的ChannelPipleline进行事件传播,ChannelPipleline也是线程安全的,数据会被传递到ChannelPipleline的第一个ChannelHandler中。数据处理完成后,将加工完成的数据再传递给下一个ChannelHandler,整个过程是串行化执行的,不不会发生线程上下文切换的问题。

NioEventLoop无锁串行化设计不仅使系统吞吐量达到最大化,而且让用户不需要花太多精力关心线程安全问题。但我们也需要意识到这种设计的缺陷:不能执行时间过长的IO操作,一旦某个IO事件发生阻塞,那么后续的所有IO事件都无法执行,最后造成事件积压,我们在使用时一定要注意这点。

任务处理机制
NioEventLoop不仅负责处理IO事件,还要兼顾执行任务队列中的任务。任务队列遵循FIFO规则,可以保证任务执行的公平性。NioEventLoop处理的任务类型基本可以分为三类:

  1. 普通任务
    通过NioEventLoop的execute方法向任务队列taskQueue中添加任务。例如Netty在写数据时会封装WriteAndFlushTask提交给taskQueue,taskQueue的实现类是多生产者单消费者队列MpsChunkedArrayQueue,在多线程并发添加任务时,可以保证线程安全。
  2. 定时任务
    通过调用NioEventLoop的schedule方法向定时任务队列shecduleTaskQueue中添加任务,用于周期性执行该任务,例如心跳消息发送等。定时任务队列shecduleTaskQueue采用优先队列PriorityQueue实现。
  3. 尾部队列
    tailTasks相比于普通任务队列优先级较低,在每次执行完taskQueue中任务后会去获取尾部队列中任务执行。尾部任务并不常用,主要用于做一些收尾工作,比如统计事件循环的执行时间、监控信息上报等。

步履不停
38 声望13 粉丝

好走的都是下坡路


« 上一篇
Netty学习二
下一篇 »
JWT学习