写在前面
上一篇讲了 I/O 模型(epoll、AIO)。但 epoll 只是底层机制,要变成一个能用的网络服务器,需要一套设计模式把"事件分发"和"业务处理"组织起来——这就是 Reactor 和 Proactor。Netty、Nginx、Redis、Kestrel 都基于这些模式。
一、Reactor 模式
1.1 核心思想
Reactor = 基于同步 I/O 多路复用的事件驱动模式。
1
2
3
4
5
6
7
8
9
10
11
12
13
| 核心组件:
1. Reactor(事件分发器)
用 epoll/select 监听所有 fd,事件就绪时分发给对应 Handler
2. Acceptor(接收器)
处理新连接(accept)
3. Handler(处理器)
处理具体连接的读写、业务
流程:
Reactor 监听 → 新连接来了,Acceptor 接收 → 注册到 Reactor
→ 数据来了,Reactor 通知 Handler → Handler 读写+处理
|
1
2
3
4
| 关键特征(同步):
I/O 操作(read/write)还是由 Handler 同步执行
Reactor 只负责"通知就绪",不做实际 I/O
本质是「事件来了 → 你自己去读」
|
1.2 三种 Reactor 实现
单 Reactor 单线程
1
2
3
4
5
6
7
8
9
10
11
12
13
| 一个线程,一个 Reactor 干所有事:
accept、read、业务处理、write 全在一个线程
┌─────────────────┐
│ Reactor(1线程) │
│ accept + IO + 业务 │
└─────────────────┘
代表:Redis 6.0 之前
优点:极简,无线程同步问题
缺点:一个连接的慢操作卡死所有连接
无法利用多核
|
单 Reactor 多线程
1
2
3
4
5
6
7
8
9
10
11
| Reactor(1线程)只负责 accept + 读写分发
业务处理交给线程池
┌──────────┐ ┌──────────────┐
│ Reactor │────→│ 业务线程池 │
│ accept+IO │ │ 多线程处理 │
└──────────┘ └──────────────┘
优点:业务多线程,利用多核
缺点:Reactor 单线程仍是瓶颈(所有 I/O 集中)
Handler 和业务线程间要同步
|
主从 Reactor 多线程(最常用)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| MainReactor 专门 accept 新连接
SubReactor(多个)处理已建立连接的读写
业务可选交给线程池
┌────────────┐
│ MainReactor │ accept
└─────┬──────┘
│ 新连接分发
┌─────▼──────┐ ┌──────────┐ ┌──────────┐
│ SubReactor │ │SubReactor │ │SubReactor │ 各自 epoll 处理 IO
└────────────┘ └──────────┘ └──────────┘
代表:Netty、Nginx(主进程accept + worker处理)
优点:充分利用多核,accept 和 IO 分离,吞吐高
这是高性能服务器的标准架构
|
二、Nginx 的 Reactor 架构
1
2
3
4
5
6
7
8
9
10
11
12
13
| Nginx = 主从 Reactor 多进程版:
Master 进程
│ accept(但不处理,转交 worker)
│
Worker 进程 ×N(= CPU 核数)
每个 Worker 自己的 epoll + Reactor
独立处理连接的读写、业务
特点:
多进程(不是多线程)→ 无锁、崩溃隔离
每个 Worker 一个 Reactor(单线程事件循环)
用共享内存(不是锁)通信
|
三、Redis 的事件模型
1
2
3
4
5
6
7
8
| Redis 6.0 之前:单 Reactor 单线程
一个线程跑事件循环 + 命令执行
为什么快:内存操作极快 + 无锁 + 无上下文切换
Redis 6.0+:单 Reactor + IO 多线程
命令执行仍单线程(保证原子、无锁)
网络 read/write 用多线程(IO 瓶颈在多核上并行)
本质:IO 用 Reactor 多线程,业务仍串行
|
四、Proactor 模式
Proactor = 基于异步 I/O 的事件驱动模式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| 核心区别(对比 Reactor):
Reactor(同步):
事件就绪 → 通知 Handler → Handler 自己 read(同步拷贝)
"数据来了,你自己读"
Proactor(异步):
发起异步 read → 内核读完+拷贝完 → 通知 Handler 处理
"数据我已经读好了,你直接用"
Proactor 流程:
1. 发起 aio_read(异步,立即返回)
2. Handler 继续干别的
3. 内核完成(等数据+拷贝)→ 通知
4. Handler 拿到的数据已就绪,直接处理
|
1
2
3
| 关键特征(异步):
I/O 操作由内核完成,Handler 只处理就绪的数据
本质是「我帮你读好了,告诉你」
|
五、Reactor vs Proactor
1
2
3
4
5
6
7
8
| Reactor Proactor
──────────────────────────────────────────────────────
I/O 模型 同步(epoll) 异步(IOCP/io_uring)
I/O 谁做 Handler 自己 内核做
通知时机 数据就绪 数据已拷贝完
实现复杂度 简单 复杂
OS 支持 Linux epoll 成熟 Windows IOCP 成熟 / Linux io_uring 新
代表 Nginx/Netty/Redis Windows IOCP、Boost.Asio
|
1
2
3
4
5
6
7
| 为什么主流还是 Reactor:
Linux 的异步 I/O(AIO)长期不成熟
io_uring(2019)才让 Linux 有了高性能异步 I/O
epoll + Reactor 已足够高效,生态成熟
Windows 用 Proactor(IOCP)
趋势:io_uring 普及后,Linux 上的 Proactor 会越来越多
|
六、实际框架对应
1
2
3
4
5
6
7
8
9
10
| 框架/系统 模式 底层
──────────────────────────────────────────────────────
Nginx 主从 Reactor 多进程 epoll
Redis Reactor(IO 多线程) epoll
Netty(Java) 主从 Reactor 多线程 epoll/IOCP
libuv(Node.js) Reactor epoll/kqueue/IOCP
Kestrel(.NET) Reactor epoll/IOCP + Pipelines
Tomcat NIO Reactor epoll/IOCP
Boost.Asio 可 Reactor/Proactor io_uring/IOCP
Windows IOCP Proactor IOCP
|
七、.NET 的 Kestrel 怎么对应
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| Kestrel 是 ASP.NET Core 的服务器,Transport 层是 Reactor:
Transport(传输层)— Reactor 模式
基于 epoll(Linux)/ IOCP(Windows)
用 System.IO.Pipelines 处理网络数据
接收连接、读取字节流
应用层 — async/await
看似 Proactor 风格(await 不阻塞)
但底层 Transport 是 Reactor(事件就绪通知)
await 在等数据时释放线程,数据就绪后继续
所以 Kestrel = Reactor(底层)+ 异步编程模型(上层)
这也是为什么 Kestrel 跨平台性能都强
|
八、小结
- Reactor:同步 I/O 多路复用 + 事件分发,“数据来了你自己读”
- 单 Reactor 单线程(Redis 6.0 前)
- 单 Reactor 多线程
- 主从 Reactor 多线程/多进程(Nginx、Netty)—— 高性能标配
- Proactor:异步 I/O,“我帮你读好了”
- Windows IOCP、Linux io_uring
- 主流是 Reactor,因为 Linux epoll 成熟;io_uring 普及后 Proactor 会更多
- Kestrel = Reactor 底层 + async/await 上层
下一篇讲零拷贝技术(sendfile/mmap/splice)——高性能网络 I/O 的另一块拼图。