前言

本文章为阅读《Linux高性能服务器编程》第八章.高性能服务器程序框架中第四小节:两种高效的事件处理模式。

Reactor模式

一、 什么是 Reactor 模式?
Reactor 翻译为“反应堆”或“反应器”。它是一种事件驱动的设计模式。

角色 职能 (任务内容) 备注
主线程 (I/O 处理单元) 调用 epoll_wait 等待事件触发;将就绪的 Socket 插入请求队列 不读写数据,不处理业务。
请求队列 存储就绪任务的缓冲区,连接 I/O 单元与逻辑单元的纽带。 典型的生产者-消费者模型。
工作线程 (逻辑单元) 从队列中取出任务;读数据 (Read) -> 逻辑处理 (Process) -> 写数据 (Write) 。 真正干重活的地方。

三、 Reactor 模式的工作流程 (同步 I/O 实现版)
image.png
以图片中的典型流程为例:

  1. 注册与监听:主线程往 epoll 事件表中注册 Socket 的读就绪事件。
  2. 就绪分发:当 Socket 有数据可读,epoll_wait 通知主线程,主线程将该事件放入请求队列
  3. 读处理
    • 某个睡眠的工作线程被唤醒。
    • 工作线程从 Socket 读取数据
    • 工作线程处理客户请求(逻辑计算)。
    • 处理完后,工作线程往 epoll 中注册该 Socket 的写就绪事件。
  4. 写处理
    • 当 Socket 可写时,epoll_wait 再次通知主线程。
    • 主线程将该写事件放入请求队列。
    • 工作线程被唤醒,往 Socket 写入结果

四、 核心设计要点

  1. 读写操作的位置
    在 Reactor 模式中,读写数据是由工作线程完成的
  1. 线程无差别化
    如图片末尾所言: Reactor 模式中,没有必要区分专门的“读线程”和“写线程”。
  1. 与 EPOLLONESHOT 的联动 (进阶要点)
    在多线程 Reactor 模型中:

Proactor模式

一、 什么是 Proactor 模式?
Proactor 模式是一种异步 I/O 模式

角色 职能 (任务内容)
主线程 / 内核 负责 I/O 监控、数据的实际读取和发送
用户缓冲区 在读写前由应用程序指定,内核直接将结果放入或取出。
信号处理函数 / 回调机制 当内核完成 I/O 后,通知应用程序。
工作线程 (逻辑单元) 仅从缓冲区拿到已经读好的数据进行业务计算,或者准备好数据交给内核去发。

三、 Proactor 模式的工作流程 (以 aio 为例)
image.png读操作流程

  1. 注册与预告:主线程调用 aio_read。不仅告诉内核要盯住哪个 Socket,还告诉内核:“这是我准备好的缓冲区地址,写完了记得用信号(或回调)通知我。”
  2. 静默执行:主线程继续处理其他事情。内核负责等待数据到达,并将数据自动拷贝到用户指定的缓冲区。
  3. 完成通知:数据进缓冲区后,内核发送信号。
  4. 业务处理:应用程序预先定义的信号处理函数(或其他机制)选择一个工作线程。工作线程直接操作缓冲区内的现成数据。

写操作流程

  1. 提交请求:工作线程处理完逻辑后,调用 aio_write,告诉内核缓冲区位置。
  2. 异步发送:主线程/内核负责将缓冲区数据发送出去。
  3. 后续扫尾:内核发送信号通知写操作完成。应用程序调用工作线程进行善后(如关闭连接)。

四、 Reactor vs Proactor (核心区别回顾)

特性 Reactor Proactor
I/O 类型 主要是同步 I/O(如 epoll) 异步 I/O(如 aio)
谁负责读写数据? 工作线程 (调用 recv/send) 内核/主线程 (完成后由内核通知)
通知时机 I/O 就绪(可以读了) I/O 完成(已经读完了)
编程难度 相对较低,容易实现 高(对异步 I/O API 的稳定性要求高)

五、 总结

同步I/O模拟Proactor

一、 核心思想
在模拟 Proactor 模式中,主线程不仅负责 I/O 监控,还亲自负责数据的读取和写入

角色 负责的任务
主线程 1. 使用 epoll_wait 监听事件。
2. 读数据:就绪后循环读取直到无数据,封装成请求包。
3. 写数据:逻辑处理完后,亲自将结果写入 Socket。
请求队列 存放主线程已经读好的“请求对象”,等待工作线程取走。
工作线程 纯逻辑处理:从队列取包 -> 逻辑运算 -> 处理结果重新交回主线程。
三、 详细工作流程(以 epoll 为例)
image.png

读事件处理流程

  1. 注册:主线程向 epoll 注册 Socket 的读就绪事件。
  2. 监听:调用 epoll_wait 等待就绪。
  3. 主线程读取:当数据可读时,主线程循环读取 Socket 数据,直到缓冲区空。
  4. 分发任务:主线程将读到的数据封装成“请求对象”,插入请求队列
  5. 业务加工:工作线程被唤醒,从队列取出对象,执行业务逻辑。

写事件处理流程

  1. 注册写事件:工作线程处理完逻辑后,向 epoll 注册该 Socket 的写就绪事件。
  2. 监听可写:主线程调用 epoll_wait 发现 Socket 可写。
  3. 主线程写入主线程亲自将处理后的结果写入 Socket,发送给客户。
  4. 扫尾:决定是否关闭连接或重置状态。

四、 关键点对比:模拟 Proactor vs. Reactor

模式 谁负责 read/write 通知内容
Reactor 工作线程 “缓冲区有数据了,你自己来读”
模拟 Proactor 主线程 “数据我已经帮你读好了,你直接处理”

五、 为什么叫“模拟”?

六、 优缺点总结