netty线程体系概览
netty的高并发能力很大程度上由它的线程模型决定的,netty定义了两种类型的线程:
I/O线程: EventLoop, EventLoopGroup。一个EventLoopGroup包含多个EventLoop, 每个Channel会被注册到一个,一个EventLoop中, 一个EventLoop可以包含多个Channel。Channel的Unsafe实例的方法必须要在EventLoop中执行(netty中明确指明的不需要在I/O线程中执行的几个方法除外,前面的章节中有详细的讲解)。
业务线程: EventExecutor, EventExecutorGroup。一个EventExecutorGroup包含多个EventExecutor。当用户向Channel的pipeline注册一个ChannelHandler时,可以指定一个EventExecutorGroup,这个ChannelHanndler的所有方法都会被放到EventExecutorGroup的中的一个EventExecutor中执行。 当用户没有为这个ChannelHandler明确指定EventExecutorGroup时,这个ChannelHandler会被放到Channel所属的EventLoop中执行。
 
为了能对netty的线程体系有一个整体的认识,笔者提供提供了一张线程的派生体系图供大家参考:
netty源码解解析(4.0)-4 线程模型-概览-LMLPHP
有上图我们可以得出这样几个有用的结论:
  1. netty的线程体系都是由EventExecutorGroup派生而来 而EventExecutorGroup派生自JDK的ScheduleExecutorService, 这表明netty的线程体系是对JDK Executor框架的扩展。
  2. netty核心的线程体系中,只提供了业务线程的最终实现: DefaultEventExecutorGroup, DefaultEvnetExecutor这两个是可以直接拿来用的。
  3. netty核心的线程体系中,为用户提供了I/O线程的框架: MultithreadEventLoopGroup, SingleThreadEventLoop, 具体I/O相关部分留给子类实现。
  4. 默认提供了用于创建线程的工厂类。
netty线性体系与JDK线程体系的对比
如果你熟悉java jdk, 就应该知道,jdk已经为开发者提供了一整套功能强大的基于多线程的Executor。那么问题来了: netty为什么要搞一套线程模型,有这个必要吗? 回答这个问题之前,我们先来对比一下两者有什么不同。
 
JDK的Executor框架
ThreadPoolExecutor是JDK Exector框架的核心实现,我们使用jdk的Executor框架,主要就是使用这个类,下面看一下这个类的实现原理
netty源码解解析(4.0)-4 线程模型-概览-LMLPHP
 
netty的EventExecutorGroup框架
MultithreadEventExecutorGroup是EventExecutorGroup框架的核心实现,这个类型的实现原理如下:
netty源码解解析(4.0)-4 线程模型-概览-LMLPHP
为了方便描述,先定义几个简称
ThreadPoolExecuto: TPE
MultithreadEventExecutorGroup: MEG
SingleThreadEventExecutor: STE
接下来,对比一下TPE和MEG
线程管理: TPE负责管理线程,根据传入的参数,运行过程中动态调节线程数,它也可以让线程一直保持在一个稳定的数量。MEG不负责管理线程,它只负责创建指定数量的STE, 每个STE只维护一个线程,保证有且只有一个线程。
任务排队: TPE维护一个所有线程共用的任务队列,所有线程都从同一队列中取任务。MEG没任务队列,它只负责把任务派发到一个STE, 默认的派发策略是轮询。每个STE维护一个私有的任务队列,STE会把任务放入私有的队列中排队,这队列只有STE维护的线程才能消费。
任务提交和执行: TPE把任务当成无关联的独立任务执行,不保证任务的执行顺序和execute的调用顺序一致, TPE认为任务的顺序不重要。MEG提交任务的方式有两种, (1)直接调用MEG的execute方法提交任务,这个方式,和TPE一样,不关心任务的执行顺序;(2)先从MEG中取出一个STE,然后调用STE的excute,这种方式任务的执行顺序和execute调用顺序一致。
性能: TPE使用共用的队列排队,在高并发环境下会导致BlockingQueue频繁的锁碰撞,进而导致大量线程切换开销,MEG中由于队列是只有一个线程消费,BlockingQueue锁碰撞机会比TPE小很多,线程切换开销也比TPE小很多,因此,可以得出结论,如果任务本身不会导致线程阻塞,MEG性能比TPE高, 否则MEG没有优势。
 
到这里已经可以回答前面提出的问题了: MEG把任务当成事件来看待,每个事件和特定的Channel关联(这一点由EventLoopGroup接口体现, 它定义了一个register(Channel channel)方法), 而一个特定Channel上触发的一系列事件,处理顺序和触发顺序必须要一致,如: 在Channel上先后触发了connect, read, close事件,如果业务上要求收到close事件后不再处理read事件, 如果执行先后顺序不能保证,很有可能执行不到read的业务。这种类似业务场景在基于TCP协议的服务器中很常见,这一点TPE不能支持,而MEG能够很好地支持这些对任务执行顺序有要求的场景。这就是netty要另外设计自己的线程模型的主要原因。
 
(注:为了能帮助读者更好地解本篇内容,接下来会补充一篇ThreadPoolExecutor代码解析的文章)
 
11-21 10:39