高性能I/O模型reactor、proactor

前言

当前主流高性能的I/O模型的实现本质是进程/线程/协程和OS I/O多路复用的一个封装,让用户只要聚焦业务处理,屏蔽网络请求处理的细节。

reactor

reactor模型是一种事件驱动的应用层I/O模型,基于分而治之和事件驱动的思想,致力于构建一个高性能的可伸缩的I/O模型。reactor模型参考资料中Doug Lea的PPT十分推荐看下,演进中的几种模型相关伪代码PPT中都有。

reactor模型无论何种实现,都会包含几个基本组件:

  • reactor: 负责I/O多路复用系统调用以及请求的分派(dispatch)。dispatch主要是根据event类型交给特定的handler处理
  • acceptor: 应用层没有accept的请求交给acceptor先进行accept系统调用
  • handler: 各种类型的handler,处理对应的event

具体用什么执行的基本对象取决于具体语言。我们假设以java的线程作为基本执行单元来说明reactor架构的演进。

reactor单线程模型

整个过程用户态只有一个线程处理。虽然底下的epoll系统调用是同步的,不过用户态可以实现成异步非阻塞的,像java中可以通过java.nio下提供的类来实现。

image.png
reactor单线程模型一个典型应用场景就是redis。redis用这个单线程模型其实没太大毛病,因为作为一个缓存系统都是比较低代价的内存操作,CPU处理不会成为瓶颈。单线程不仅没有资源共享导致的竞态问题也没有额外的上下文切换开销,是合适的选择。

redis6.0以后引入了多线程的I/O模型其实和以前redis的设计理念不冲突。当一个redis服务大量的client的时,网络读写会成为一个瓶颈,这时候引入多线程I/O肯定是合理的。

reactor多线程模型

该实现主要有以下好处:

  • 单线程变成多线程处理提升处理性能
  • 将非I/O相关的处理offload出去,交给一个工作线程池处理

仍然存在的问题:单个reactor有单点问题,会成为性能瓶颈。并发的新连接特别多的时候,accept会成为瓶颈。

image.png

reactor主从模型

实际上是多reactor的实现,引入了reactor pool来提升accept的效率。注意下图中的subreactor可以有多个,下图只演示了一个sub reactor thread。
image.png
netty有主从模型reactor的实现,有兴趣可以阅读下源码。

reactor模型在项目中的应用

以下知名项目都涉及reactor模型的实现,可以参考:

  • netty: 单线程、多线程、主从模型都有实现
  • memcached: 多线程reactor模型
  • nginx: 多进程reactor模型
  • redis: 单线程reactor模型

proactor

基于epoll的reactor模式是同步的I/O模型。proactor指的是异步I/O模型。proactor需要OS层面的支持。和reactor区别是网络I/O的数据准备阶段和数据拷贝阶段都是可异步化的。
image.png

  • Reactor 是非阻塞同步网络模式,感知的是就绪可读写事件。在每次感知到有事件发生(比如可读就绪事件)后,就需要应用进程主动调用 read 方法来完成数据的读取,也就是要应用进程主动将 socket 接收缓存中的数据读到应用进程内存中,这个过程是同步的,读取完数据后应用进程才能处理数据。
  • Proactor 是异步网络模式, 感知的是已完成的读写事件。在发起异步读写请求时,需要传入数据缓冲区的地址(用来存放结果数据)等信息,这样系统内核才可以自动帮我们把数据的读写工作完成,这里的读写工作全程由操作系统来做,并不需要像 Reactor 那样还需要应用进程主动发起 read/write 来读写数据,操作系统完成读写工作后,就会通知应用进程直接处理数据。

参考资料