什么是 Reactor 模式?
Reactor 模式是一种用于处理服务端高并发的事件驱动架构模式,它的核心思想是“一个或多个线程(Reactor)负责监听和分发事件,而真正处理事件的逻辑则由其他线程(Worker)执行”。

可以把它想象成一个餐厅的经理(Reactor)和服务员(Worker):
-
Reactor (经理):
- 他的唯一职责就是站在门口,观察是否有客人(事件)到来。
- 如果有客人来了,他不会亲自招待,而是根据客人的需求(事件类型),把任务单(请求)交给一个空闲的服务员(Worker 线程)去处理。
- 他自己则继续站在门口,继续观察下一个客人。
-
Worker (服务员):
- 他们专门负责具体的招待工作(处理业务逻辑),比如点餐、上菜。
- 他们从经理那里拿到任务单,执行完毕后,再向经理报告自己已经空闲,可以接受下一个任务了。
这种模式的好处是:

- 非阻塞:Reactor 线程不会被任何 I/O 操作阻塞,它可以高效地同时监听成千上万个连接。
- 高吞吐量:因为线程不会被阻塞,所以可以处理非常多的并发请求。
- 资源高效:相比传统的“一个连接一个线程”模型(BIO),它极大地减少了线程数量,避免了线程切换带来的巨大开销。
Reactor 模式的核心组件
一个标准的 Reactor 模式通常包含以下几个核心组件:
-
Reactor (反应器):
- 这是模式的核心,它是一个对象,负责监听注册在它上面的所有 I/O 通道(如 Socket)。
- 当它检测到某个通道上有“就绪”事件(如可读、可写)发生时,它会将事件分发给对应的处理器。
- 在 Java NIO 中,Reactor 的角色通常由
Selector来扮演。
-
Demultiplexer (多路复用器):
- 这是 Reactor 的“眼睛”和“耳朵”,它负责从多个 I/O 通道中“多路复用”出已经准备就绪的通道。
- 在 Java NIO 中,
SelectorDemultiplexer的具体实现,它可以同时监视多个SelectableChannel的 I/O 状态。
-
Event Handler (事件处理器):
- 它是一个接口或抽象类,定义了如何处理特定类型的事件。
- 当 Reactor 分发事件时,会调用
Event Handler中对应的方法(如onRead(),onWrite())。 - 每个连接都会有一个对应的事件处理器实例。
-
Synchronous Event Demultiplexer (同步事件多路复用器):
- 这是一个等待事件发生的机制,它会阻塞,直到有至少一个它监视的通道上发生了事件。
- 在 Java NIO 中,
Selector.select()方法就是这个角色。
-
Initiation Dispatcher (初始化分发器):
- 它负责管理
Event Handler的注册、注销,并在事件发生时,从Demultiplexer获取事件,然后查找并调用相应的Event Handler。 - 在 Java NIO 中,这部分逻辑通常由开发者自己编写,通过
SelectionKey来关联Channel和Handler。
- 它负责管理
Reactor 模型的演变(单线程 vs. 多线程)
Reactor 模式根据线程模型的不同,主要分为三种实现方式。
1 单 Reactor 单线程模型
这是最简单的模型,所有的 I/O 操作(包括 accept、read、write)和业务逻辑处理都在同一个线程中完成。
- 流程:
- Reactor 线程通过
Selector监听所有事件。 - 当有事件发生时,Reactor 线程调用对应的
Handler来处理。 - 如果是
ACCEPT事件,则建立连接,并将新连接的Channel注册到Selector上,监听READ事件。 - 如果是
READ事件,则读取数据,并处理业务逻辑。 - 如果业务逻辑中需要写回响应,则直接在
Handler中进行WRITE操作。
- Reactor 线程通过
- 优点:
模型简单,没有多线程竞争和同步的问题。
- 缺点:
- 性能瓶颈严重,当某个
Handler处理缓慢时(如执行耗时业务逻辑),会阻塞整个Selector,导致所有客户端的请求都被延迟。 - 无法充分利用多核 CPU 的性能。
- 性能瓶颈严重,当某个
- 适用场景:
- CPU 密集型且非常轻量级的业务场景。
- 如今已很少使用。
2 单 Reactor 多线程模型
为了解决单线程模型的性能瓶颈,出现了这个模型,I/O 操作仍然由 Reactor 线程处理,但业务逻辑处理交给了一个 Worker 线程池。
- 流程:
- Reactor 线程通过
Selector监听所有事件。 - 当有
READ事件发生时,Reactor 线程从Channel中读取数据,但不进行业务处理。 - Reactor 线程将读取到的数据作为一个任务,提交给 Worker 线程池。
- Worker 线程池中的某个空闲线程会从队列中取出任务,并执行业务逻辑。
- 业务逻辑处理完成后,如果需要写回响应,Worker 线程会将写回操作作为一个任务,提交给一个任务队列(或者直接通过某种方式让 Reactor 线程执行)。
- Reactor 线程(或专门的 I/O 线程)从任务队列中取出写任务,执行
WRITE操作。
- Reactor 线程通过
- 优点:
- 利用多线程并行处理业务逻辑,提高了 CPU 利用率和吞吐量。
- Reactor 线程仍然只负责 I/O,不会被业务逻辑阻塞。
- 缺点:
- Reactor 线程仍然是单点,如果连接数非常多,
Selector.select()和分发事件可能会成为瓶颈。
- Reactor 线程仍然是单点,如果连接数非常多,
- 适用场景:
- 一个 Reactor 线程足够处理所有 I/O 操作的场景。
- Netty 的
NioEventLoopGroup在默认情况下,如果只有一个 boss 线程,就类似于这个模型。
3 主从 Reactor 多线程模型
这是目前最流行、性能最高的模型,也是 Netty 框架采用的默认模型,它将 Reactor 分成了两组:MainReactor (主 Reactor) 和 SubReactor (从 Reactor)。
- 流程:
- MainReactor:通常只有一个线程,它不负责处理已连接的 Socket,而是只负责监听服务器端口的
ACCEPT事件。 - 当有新的客户端连接到来时,MainReactor 通过
ACCEPT事件获取到SocketChannel。 - MainReactor 将这个
SocketChannel注册到一个从 Reactor (SubReactor) 上,并监听其READ事件。 - SubReactor:通常是一个线程池,每个 SubReactor 线程独立管理一组
SocketChannel,负责处理这些通道上的 I/O 事件(如READ,WRITE)。 - 当 SubReactor 监听到某个
SocketChannel有READ事件时,它会读取数据,然后将任务提交给 Worker 线程池进行业务逻辑处理。 - 处理完成后,Worker 线程将写回任务提交给 SubReactor,由其执行
WRITE操作。
- MainReactor:通常只有一个线程,它不负责处理已连接的 Socket,而是只负责监听服务器端口的
- 优点:
- 极高的并发处理能力:MainReactor 专注于新连接的接入,SubReactor 专注于已连接 I/O 的处理,分工明确。
- 可扩展性强:可以通过增加 SubReactor 的数量来充分利用多核 CPU,线性提升性能。
- 消除单点瓶颈:不再有单个 Reactor 线程成为性能瓶颈。
- 缺点:
模型相对复杂,实现起来更麻烦。
- 适用场景:
高并发、高性能的服务端应用,如 Netty、Nginx 等都采用了此模型。
Java NIO 与 Reactor 模式的关系
Java NIO (New I/O) 是实现 Reactor 模式的完美基石。
- Channel (通道):对应网络连接,可以被设置为非阻塞模式。
- Buffer (缓冲区):数据读写都通过 Buffer 进行,是数据的容器。
- Selector (选择器):这就是
Demultiplexer和Reactor的核心,它可以注册多个SelectableChannel,并阻塞地等待这些通道上的 I/O 事件。 - SelectionKey (选择键):代表了
Channel和Selector的一种注册关系,它包含了事件类型(OP_READ,OP_WRITE等)和关联的Attachment(通常是Event Handler)。
Reactor 模式在 Java NIO 中的简单实现步骤:
- 创建一个
Selector和一个ServerSocketChannel。 - 将
ServerSocketChannel设置为非阻塞模式,并绑定到指定端口。 - 将
ServerSocketChannel注册到Selector上,监听OP_ACCEPT事件。 - 进入一个无限循环,调用
selector.select()阻塞等待事件。 - 当有事件发生时,
selector.select()返回,通过selector.selectedKeys()获取所有就绪的SelectionKey。 - 遍历
SelectionKey集合:- 如果是
OP_ACCEPT事件,则接受新连接,得到SocketChannel,将其设置为非阻塞模式,并注册到Selector上监听OP_READ事件,同时可以附上一个Handler对象。 - 如果是
OP_READ事件,则通过SelectionKey获取到SocketChannel和Handler,调用Handler的read()方法读取数据。
- 如果是
- 处理完事件后,从
selectedKeys集合中移除当前SelectionKey。
Reactor 模式 vs. 传统 BIO 模式
| 特性 | 传统 BIO 模式 | Reactor (NIO) 模式 |
|---|---|---|
| I/O 模型 | 阻塞 I/O | 非阻塞 I/O |
| 线程模型 | 一个连接一个线程 | 一个或少量线程管理大量连接 |
| 并发能力 | 弱,线程数受限于内存 | 强,轻松处理成千上万连接 |
| CPU 利用率 | 低,线程大部分时间在阻塞等待 | 高,线程不被 I/O 阻塞 |
| 编程复杂度 | 相对简单 | 相对复杂,需要处理状态、缓冲区等 |
| 代表框架 | Tomcat (默认模式,可配置NIO) | Netty, Vert.x, Nginx |
Reactor 模式是构建高性能、高并发 Java 服务的基石,它通过事件驱动和非阻塞 I/O 的方式,解决了传统 BIO 模型中线程资源浪费和并发能力低下的问题。
- 核心思想:用少量线程高效地管理大量连接,将 I/O 操作和业务逻辑处理分离。
- 演进:从简单的单线程模型,到性能更好的多线程模型,再到目前最为主流和强大的主从多线程模型。
- Java 实现:Java NIO 提供了
Selector,Channel,Buffer等核心组件,是实践 Reactor 模式的强大工具。 - 实际应用:理解 Reactor 模式对于学习和使用 Netty 框架至关重要,因为 Netty 的整个架构就是基于主从 Reactor 模式设计的,掌握它,你就能更好地理解 Netty 的工作原理,并写出更高效、更健壮的网络程序。
