Reactor 模式详解
Reactor(反应器模式) 是一种 事件驱动的设计模式,用于处理高并发 I/O 请求,核心思想是 通过事件分发机制,用单线程或多线程高效处理大量连接。它是 Nginx、Redis、Netty 等高性能系统的底层基础。
1. 核心思想
- “非阻塞 I/O + 事件通知”:
- 所有 I/O 操作设置为非阻塞。
- 通过 事件循环(Event Loop) 监听 I/O 事件(如可读、可写),由 Reactor 统一分发到对应的处理器(Handler)。
- 避免线程阻塞:
传统多线程模型中,每个连接需要一个线程,而 Reactor 用少量线程(甚至单线程)处理所有连接。
2. 核心组件
组件 | 作用 |
---|---|
Reactor | 事件循环核心,监听并分发 I/O 事件(如 select /epoll /kqueue )。 |
Handler | 事件处理器,处理具体的 I/O 操作(如读取数据、业务逻辑)。 |
Demultiplexer | 系统级事件监听器(如 Linux 的 epoll ),通知 Reactor 哪些事件已就绪。 |
3. 工作流程
- 注册事件:
- 将需要监听的 I/O 事件(如 Socket 可读)注册到 Demultiplexer。
- 事件循环:
- Reactor 调用
epoll_wait()
等接口阻塞等待事件就绪。
- Reactor 调用
- 事件分发:
- 当某个 Socket 可读/可写时,Demultiplexer 通知 Reactor。
- 处理事件:
- Reactor 将事件分发给对应的 Handler 执行实际读写或业务逻辑。
+-------------------+ +-------------------+
| Client Socket | <---> | Reactor |
+-------------------+ | (Event Loop) |
| - epoll_wait() |
| - Dispatch Events|
+--------+----------+
|
+--------v----------+
| Handler |
| - read()/write()|
| - Business Logic|
+-------------------+
4. Reactor 的线程模型
根据业务复杂度,Reactor 可分为以下变体:
(1) 单线程模型(Basic Reactor)
- 所有操作在单线程中完成(事件监听、I/O 处理、业务逻辑)。
- 优点:无锁、无上下文切换,极致轻量(如 Redis 单线程)。
- 缺点:若 Handler 处理慢,会阻塞整个事件循环。
- 适用场景:Handler 逻辑简单且快速(如内存操作)。
(2) 多线程 Reactor(Multithreaded Reactor)
- 单 Reactor 线程 + 线程池:
- Reactor 线程仅负责事件监听和分发。
- Handler 的耗时业务逻辑交给线程池处理(避免阻塞事件循环)。
- 优点:充分利用多核 CPU。
- 缺点:线程间共享数据需加锁。
- 示例:早期 Netty 的
OioEventLoop
。
(3) 主从 Reactor(Master-Slave Reactor)
- 主 Reactor:负责监听连接事件(如
accept()
),分发给 子 Reactor。 - 子 Reactor:负责监听已建立连接的 I/O 事件(如
read()
/write()
)。 - 优点:职责分离,避免连接建立和数据处理相互阻塞。
- 示例:Netty 的
NioEventLoopGroup
、Nginx。
+---------------------+
| Main Reactor | (监听 Accept 事件)
| - 1 个线程 |
+----------+----------+
|
+----------v----------+
| Sub Reactors | (监听读写事件)
| - N 个线程 |
+---------------------+
5. 为什么用 Reactor?
对比传统阻塞模型
模型 | 线程开销 | 并发能力 | 适用场景 |
---|---|---|---|
阻塞式多线程 | 1 连接 = 1 线程 | 低(C10K 问题) | 低并发简单应用 |
Reactor | 1 线程处理多连接 | 高(单机数万连接) | 高并发 I/O 密集型系统 |
优势
- 高吞吐:通过事件驱动避免线程阻塞。
- 低延迟:快速响应 I/O 事件。
- 可扩展性:通过线程池灵活应对计算密集型任务。
6. 实际应用
(1) Redis
- 单线程 Reactor:所有操作在单线程中串行执行,避免锁竞争。
- 高性能原因:纯内存操作 + 事件驱动。
(2) Netty
- 主从 Reactor + 线程池:
BossGroup
(主 Reactor)处理连接。WorkerGroup
(子 Reactor)处理 I/O。- 业务逻辑可提交到自定义线程池。
(3) Nginx
- 多 Worker 进程:每个 Worker 是独立的 Reactor,通过
epoll
处理连接。
7. 代码示例(简化版 Reactor)
// 基于 Java NIO 的简易 Reactor 实现
public class Reactor implements Runnable {
private final Selector selector;
private final ServerSocketChannel serverSocket;
public Reactor(int port) throws IOException {
selector = Selector.open();
serverSocket = ServerSocketChannel.open();
serverSocket.socket().bind(new InetSocketAddress(port));
serverSocket.configureBlocking(false);
// 注册 Accept 事件到 Reactor
SelectionKey sk = serverSocket.register(selector, SelectionKey.OP_ACCEPT);
sk.attach(new Acceptor()); // 设置处理器
}
@Override
public void run() {
try {
while (!Thread.interrupted()) {
selector.select(); // 阻塞等待事件
Set<SelectionKey> selected = selector.selectedKeys();
for (SelectionKey key : selected) {
dispatch(key); // 分发事件
}
selected.clear();
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 事件分发
void dispatch(SelectionKey key) {
Runnable handler = (Runnable) key.attachment();
if (handler != null) {
handler.run(); // 执行处理器
}
}
// 处理 Accept 事件的处理器
class Acceptor implements Runnable {
@Override
public void run() {
try {
SocketChannel client = serverSocket.accept();
if (client != null) {
new Handler(selector, client); // 创建 Handler 处理读写
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
8. 总结
- Reactor 本质:事件驱动 + 非阻塞 I/O + 高效事件分发。
- 核心价值:用少量线程处理海量连接,解决 C10K 问题。
- 选择建议:
- 低延迟简单逻辑 → 单线程 Reactor(如 Redis)。
- 高并发复杂业务 → 主从 Reactor + 线程池(如 Netty)。