参考文献

  • Scalable IO in Java --Doug Lea

线程模型

  • 基本的线程池化模式可以描述为
    • 从池的空闲线程列表中选择一个Thread,并且指派它去运行一个已提交的任务(一个Runnable的实现);
    • 当任务完成时,将该Thread返回给该列表,使其可被重用。

Reactor线程模式

  • Reactor线程模式是一种基于事件驱动的编程模式.

  • 它将事件的处理分解为两个基本操作:事件的分发和事件的处理.

  • Reactor模式通常包括以下角色:

    • Reactor:Reactor是整个模式的核心,它负责监听和分发事件,将事件分发给相应的处理器进行处理.Reactor通常使用事件循环来轮询事件队列,将事件分发给相应的处理器.

    • Handler:Handler是事件处理器,负责处理具体的事件.当Reactor将事件分发给Handler时,Handler会执行相应的业务逻辑,并将处理结果返回给Reactor.

    • Acceptor :Acceptor 是连接处理器,负责处理新的连接请求.当有新的连接请求到来时,Acceptor 会创建一个新的连接,并将其注册到Reactor中.

    • Connector :Connector 是连接器,负责建立与远程服务器的连接.当需要与远程服务器通信时,Connector 会创建一个新的连接,并将其注册到Reactor中.

    • Dispatcher :Dispatcher 是事件分发器,负责将事件分发给相应的处理器.Dispatcher 通常使用线程池来管理处理器线程,可以避免线程创建和销毁的开销,提高并发性能和可伸缩性.

Reactor的分类

Reactor单线程模式

  • Reactor单线程模式:整个应用程序只有一个Reactor线程和一个事件循环来处理所有的事件,适合于低并发和短时间任务的场景.
  • 当有新的事件到来时,该事件会被放入到事件队列中,Reactor 线程会轮询该队列,并将事件分发给相应的处理器进行处理.
  • 这种模式的优点是简单、易于实现,但并发性能受限,不能充分利用多核 CPU 的优势,适用于对性能要求不高的场景,如 Redis 单线程模式.

摘自Doug Lea.Scalable IO In Java

Reactor多线程模式

  • Reactor多线程模式:整个应用程序只有一个Reactor线程和多个工作线程来处理所有的事件,适合于高并发和长时间任务的场景.
  • 当有新的事件到来时,该事件会被放入到事件队列中,Reactor 线程会轮询该队列,并将事件分发给空闲的工作线程进行处理.
  • 这种模式的优点是可以利用多核 CPU 的优势,提高并发性能和可伸缩性,但需要处理线程之间的同步和数据共享问题,适用于对性能要求高的场景,如 Netty 多线程模式.

摘自Doug Lea.Scalable IO In Java

主从Reactor多线程模式

  • 主从Reactor多线程模式:整个应用程序有一个主Reactor线程和多个从Reactor线程来处理所有的事件,适合于高并发和长时间任务的场景.
  • 当有新的事件到来时,主Reactor线程会将事件分发给从Reactor线程进行处理,从Reactor线程可以使用多线程模式来处理事件.
  • 这种模式的优点是可以有效避免锁竞争和线程之间的同步问题,提高并发性能和可伸缩性,适用于对性能和可靠性要求高的场景,如 Nginx主从Reactor多线程模式.

摘自Doug Lea.Scalable IO In Java

NettyReactor线程模式

  • Netty推荐采用主从多线程模型.
  • Reactor线程模型运行机制的四个步骤
    • 连接注册: Channel建立后,注册至Reactor线程中的Selector选择器
    • 事件轮询: 轮询Seletor选择器中已注册的所有ChannelI/O事件
    • 事件分发: 为准备就绪的I/O事件分配相应的处理线程
    • 任务处理: Reactor线程还负责任务队列中的非I/O任务,每个Worker线程从各自维护的任务队列中取出任务异步执行.
Reactor角色 Netty的组件
Reactor NioEventLoop
Handler ChannelHandler接口实现类
Acceptor ServerBootstrapServerChannelInitializer
Connector ChannelInitializer
Dispatcher EventExecutorGroupThreadPoolExecutor
  • NioEventLoop负责监听和分发事件
  • ServerBootstrapBootstrap用于创建服务器和客户端连接
  • ChannelHandler处理事件
  • EventExecutorGroupThreadPoolExecutor管理处理器线程和线程池
img
  • BossEventLoopGroupWorkerEventLoopGroup包含一个或者多个NioEventLoop.BossEventLoopGroup负责监听客户端的Accept事件,当事件触发时,将事件注册至WorkerEventLoopGroup中的一个NioEventLoop上.每新建一个Channel,只选择一个NioEventLoop与其绑定.即Channel生命周期的所有事件处理都是线程独立的,不同的NioEventLoop线程之间不会发生任何交集.

  • NioEventLoop完成数据读取后,会调用绑定的ChannelPipeline进行事件传播,ChannelPipeline也是线程安全的,数据会被传递到ChannelPipeline的第一个ChannelHandler中.数据处理完成后,将加工完成的数据再传递给下一个ChannelHandler,整个过程是串行化执行的,不会发生线程上下文切换的问题.

  • NioEventLoop无锁串行化的设计不仅使系统吞吐量达到最大化,而且降低了用户开发业务逻辑的难度,不需要花太多精力关心线程安全问题.虽然单线程执行避免了线程切换,但是它的缺陷就是不能执行时间过长的I/O操作,一旦某个I/O事件发生阻塞,那么后续的所有I/O事件都无法执行,甚至造成事件积压.在使用Netty进行程序开发时,一定要对ChannelHandler的实现逻辑有充分的风险意识.

    如果ChannelHandler逻辑非常复杂而且是阻塞的,那么请用

    • ChannelPipeline addFirst(EventExecutorGroup group, String name, ChannelHandler handler);
    • ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler);
    • 如果你的项目无需考虑ChannelHandler的执行顺序,可以用UnorderedThreadPoolEventExecutor这个线程组以达到最高的并发度