Reactor 线程模式
1. 概念

Reactor 模式有两个核心组成部分:
Reactor(图中的ServiceHandler):Reactor在一个单独的线程中运行,负责监听和分发事件,分发给适当的处理线程来对 IO 事件做出反应。Handlers(图中的EventHandler):处理线程执行处理方法来响应 I/O 事件,处理线程执行的是非阻塞操作。
2. 模式
2.1. 单 Reactor 单线程模式

这种模式的基本工作流程为:
Reactor通过select监听客户端请求事件,收到事件之后通过dispatch进行分发- 如果事件是建立连接的请求事件,则由
Acceptor通过accept处理连接请求,然后创建一个Handler对象处理连接建立后的后续业务处理。 - 如果事件不是建立连接的请求事件,则由
Reactor对象分发给连接对应的Handler处理。 Handler会完成read-->业务处理-->send的完整处理流程。
| 优点 | 缺点 |
|---|---|
| 模型简单,没有多线程、进程通信、竞争的问题,一个线程完成所有的事件响应和业务处理 | 存在性能问题,只有一个线程,无法完全发挥多核 CPU 的性能。存在可靠性问题,若线程意外终止,或者进入死循环,会导致整个系统通信模块不可用,不能接收和处理外部消息,造成节点故障 |
适用场景:
客户端的数量有限,业务处理非常快速,比如 Redis 在业务处理的时间复杂度为 O(1)的情况。
2.2. 单 Reactor 多线程模式

这种模式的基本工作流程为:
Reactor对象通过select监听客户端请求事件,收到事件后通过dispatch进行分发。- 如果事件是建立连接的请求事件,则由
Acceptor通过accept处理连接请求,然后创建一个Handler对象处理连接建立后的后续业务处理。 - 如果事件不是建立连接的请求事件,则由
Reactor对象分发给连接对应的Handler处理。Handler只负责响应事件,不做具体的业务处理,Handler通过read读取到请求数据后,会分发给后面的Worker线程池来处理业务请求。 Worker线程池会分配独立线程来完成真正的业务处理,并将处理结果返回给Handler。Handler通过send向客户端发送响应数据。
| 优点 | 缺点 |
|---|---|
| 充分的利用多核 cpu 的处理能力 | 多线程数据共享和控制比较复杂,Reactor 处理所有的事件的监听和响应,在单线程中运行,面对高并发场景还是容易出现性能瓶颈 |
2.3. 主从 Reactor 多线程模式

这种模式的基本工作流程为:
Reactor主线程MainReactor对象通过select监听客户端连接事件,收到事件后,通过Acceptor处理客户端连接事件。- 当
Acceptor处理完客户端连接事件之后(与客户端建立好Socket连接),MainReactor将连接分配给SubReactor。(即:MainReactor只负责监听客户端连接请求,和客户端建立连接之后将连接交由SubReactor监听后面的 IO 事件。) SubReactor将连接加入到自己的连接队列进行监听,并创建Handler对各种事件进行处理。- 当连接上有新事件发生的时候,
SubReactor就会调用对应的Handler处理。 Handler通过read从连接上读取请求数据,将请求数据分发给Worker线程池进行业务处理。Worker线程池会分配独立线程来完成真正的业务处理,并将处理结果返回给 Handler。Handler通过send向客户端发送响应数据。- 一个
MainReactor可以对应多个SubReactor,即一个MainReactor线程可以对应多个SubReactor线程。
| 优点 | 缺点 |
|---|---|
1. MainReactor 线程与 SubReactor 线程的数据交互简单职责明确,MainReactor 线程只需要接收新连接,SubReactor 线程完成后续的业务处理。2. MainReactor 线程与 SubReactor 线程的数据交互简单, MainReactor 线程只需要把新连接传给 SubReactor 线程,SubReactor 线程无需返回数据 3. 多个 SubReactor 线程能够应对更高的并发请求 |
编程复杂度较高 |
这种模式也被叫做服务器的 1+M+N 线程模式,即使用该模式开发的服务器包含1个(或多个,1 只是表示相对较少)连接建立线程 + M个IO线程 + N个业务处理线程。这是业界成熟的服务器程序设计模式。
Netty 架构
1. 简版

BossGroup线程维护Selector,ServerSocketChannel注册到这个Selector上,只关注连接建立请求事件(相当于MainReactor)。- 当接收到来自客户端的连接建立请求事件的时候,通过
ServerSocketChannel.accept方法获得对应的SocketChannel,并封装成NioSocketChannel注册到WorkerGroup线程中的Selector,每个Selector运行在一个线程中(相当于SubReactor)。 - 当
WorkerGroup线程中的Selector监听到自己感兴趣的 IO 事件后,就调用Handler进行处理。
2. 第二版

- 有两组线程池:
BossGroup和WorkerGroup:
BossGroup中的线程(可以有多个,图中只画了一个)专门负责和客户端建立连接;WorkerGroup中的线程专门负责处理连接上的读写。
BossGroup和WorkerGroup含有多个不断循环的执行事件处理的线程,每个线程都包含一个Selector,用于监听注册在其上的Channel。- 每个
BossGroup中的线程循环执行以下三个步骤:
3.1. 轮训注册在其上的ServerSocketChannel的accept事件(OP_ACCEPT 事件)
3.2. 处理accept事件,与客户端建立连接,生成一个NioSocketChannel,并将其注册到WorkerGroup中某个线程上的Selector上
3.3. 再去以此循环处理任务队列中的下一个事件 - 每个
WorkerGroup中的线程循环执行以下三个步骤:
4.1. 轮训注册在其上的NioSocketChannel的read/write事件(OP_READ/OP_WRITE 事件)
4.2. 在对应的NioSocketChannel上处理read/write事件
4.3. 再去以此循环处理任务队列中的下一个事件
3. 第三版

Netty抽象出两组线程池:BossGroup和WorkerGroup,也可以叫做BossNioEventLoopGroup和WorkerNioEventLoopGroup。每个线程池中都有NioEventLoop线程。BossGroup中的线程专门负责和客户端建立连接,WorkerGroup中的线程专门负责处理连接上的读写。BossGroup和WorkerGroup的类型都是NioEventLoopGroup。NioEventLoopGroup相当于一个事件循环组,这个组中含有多个事件循环,每个事件循环就是一个NioEventLoop。NioEventLoop表示一个不断循环的执行事件处理的线程,每个NioEventLoop都包含一个Selector,用于监听注册在其上的Socket网络连接(Channel)。NioEventLoopGroup可以含有多个线程,即可以含有多个NioEventLoop。- 每个
BossNioEventLoop中循环执行以下三个步骤:
5.1.select:轮训注册在其上的ServerSocketChannel的accept事件(OP_ACCEPT 事件)
5.2.processSelectedKeys:处理accept事件,与客户端建立连接,生成一个NioSocketChannel,并将其注册到某个WorkerNioEventLoop上的Selector上
5.3.runAllTasks:再去以此循环处理任务队列中的其他任务 - 每个
WorkerNioEventLoop中循环执行以下三个步骤:
6.1.select:轮训注册在其上的NioSocketChannel的read/write事件(OP_READ/OP_WRITE 事件)
6.2.processSelectedKeys:在对应的NioSocketChannel上处理read/write事件
6.3.runAllTasks:再去以此循环处理任务队列中的其他任务 - 在以上两个
processSelectedKeys步骤中,会使用Pipeline(管道),Pipeline中引用了Channel,即通过Pipeline可以获取到对应的Channel,Pipeline中维护了很多的处理器(拦截处理器、过滤处理器、自定义处理器等)。
