知识卡片

单Reactor单进程与单Reactor多线程的取舍

结构图卡

内容

[[I/O多路复用的由来:从资源池到事件通知机制]]结合线程池后诞生了Reactor模式,本质是”来了事件我通知你,你来处理”——Reactor负责用select/epoll等机制统一监听所有连接上的事件,一旦有事件就通过dispatch分发给对应的处理逻辑(这也是Reactor又被叫做Dispatcher模式的原因)。Reactor模式的具体实现要看两个维度的组合:Reactor本身的数量(单个还是多个)、处理资源池的数量(单进程/线程还是多进程/线程),理论上有四种组合,但”多Reactor单进程”既复杂又没有性能优势,实践中不存在,真正落地的只有三种典型方案。第一种是单Reactor单进程/线程:Reactor通过select监听事件并dispatch,连接建立事件交给Acceptor处理(accept连接并创建对应的Handler),其余事件交给对应连接的Handler处理,Handler完整走完read→业务处理→send整个流程,全过程都在一个进程/线程里完成,好处是简单,没有进程间通信、没有资源竞争;坏处也很直接——单进程发挥不出多核CPU的性能(想用多核只能在一台机器上部署多套系统,运维复杂度陡增),而且Handler正在处理某个连接的业务时,整个进程没法处理其他连接的事件,很容易撞上性能瓶颈,因此这个方案只适合业务处理本身极快的场景,著名的开源实现是Redis(C语言系统一般用单Reactor单进程,Java系统因为JVM本身就是一个进程、内部本来就是多线程,一般用单Reactor单线程)。第二种是单Reactor多线程:为了发挥多核性能,把”业务处理”从Reactor所在的主线程里剥离出去——Handler只负责响应事件、读取数据,真正的业务处理交给独立的子线程(Processor)去做,处理完把结果传回主线程的Handler,再由Handler通过send返回给客户端;这个方案确实能利用多核,但也带来两个新问题:多线程间共享数据的互斥和保护变复杂了(比如Java NIO里Selector本身线程安全,但selectKeys()返回的键集合并非线程安全,处理时必须单线程或加同步保护);而且Reactor本身仍然只跑在主线程里、独自承担全部事件的监听和分发,遇到瞬间高并发依然会成为瓶颈。这里没有对应的”单Reactor多进程”方案,原因在于子进程完成业务处理后要把结果传回父进程、再由父进程发给对应client,这件事本身很麻烦——父子进程通信不是一条天然的”连接”,要模拟成连接纳入Reactor监听会相当复杂;而多线程天然共享数据,线程间传递结果虽然要处理同步问题,但复杂度比进程间通信低得多,这正是”单Reactor多进程”没被实际采用的根本原因。

结构图

flowchart TB
  A["Reactor模式:I/O多路复用统一监听事件+dispatch分发"]
  A --> B["单Reactor单进程/线程"]
  B --> B1["优点:简单,无进程间通信/无资源竞争"]
  B --> B2["缺点:发挥不出多核CPU<br/>处理某连接时无法处理其他连接事件"]
  B2 --> B3["适用:业务处理极快场景,如Redis"]
  A --> C["单Reactor多线程"]
  C --> C1["Handler只负责收发,业务处理交给独立子线程Processor"]
  C1 --> C2["优点:能利用多核CPU"]
  C1 --> C3["缺点:多线程共享数据需同步保护<br/>Reactor仍单点,瞬间高并发时是瓶颈"]
  A --> D["为何没有'单Reactor多进程'方案?<br/>子进程结果传回父进程再转发,父子间不是天然连接<br/>模拟成连接纳入监听过于复杂,远不如线程间天然共享数据简单"]

参考来源

- 位置:《从零开始学架构》第19讲《单服务器高性能模式:Reactor与Proactor》"Reactor""单Reactor单进程/线程""单Reactor多线程"(源文件:_epub-src/OEBPS/text00001.html) - 结论依据:原文说明单Reactor单进程"只有一个进程,无法发挥多核 CPU 的性能……只适用于业务处理非常快速的场景,目前比较著名的开源软件中使用单 Reactor 单进程的是 Redis",单Reactor多线程"能够充分利用多核多 CPU 的处理能力,但同时也存在……多线程数据共享和访问比较复杂……Reactor 承担所有事件的监听和响应,只在主线程中运行,瞬间高并发时会成为性能瓶颈",并解释"如果采用多进程,子进程完成业务处理后,将结果返回给父进程……是很麻烦的事情……而采用多线程时,因为多线程是共享数据的,因此线程间通信是非常方便的",直接支撑本卡片结论与结构图。 - 原始内容:只有一个进程,无法发挥多核 CPU 的性能……单 Reactor 多线程方案能够充分利用多核多 CPU 的处理能力,但同时也存在下面的问题……如果采用多进程,子进程完成业务处理后,将结果返回给父进程……是很麻烦的事情。