操作系统学习笔记(三):Reactor 与 Proactor 模式

写在前面

上一篇讲了 I/O 模型(epoll、AIO)。但 epoll 只是底层机制,要变成一个能用的网络服务器,需要一套设计模式把"事件分发"和"业务处理"组织起来——这就是 ReactorProactor。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 的另一块拼图。