IO线程模型之Reactor 和 Proactor

152 阅读4分钟

image.png

Reactor 和 Proactor 是两个高性能网络模式。 别小看这两个东西,特别是 Reactor 模式,市面上常见的开源软件很多都采用了这个方案,比如 Redis、Nginx、Netty 等等,所以学好这个模式设计的思想,不仅有助于我们理解很多开源软件。

Reactor模型

是一种基于I/O多路复用的线程模型,通过事件驱动和线程池来提高系统的性能和可扩展性

组成

  • Reactor:负责监听和分发事件,事件类型包含连接事件、读写事件。
  • 处理资源池:负责处理事件,如 read -> 业务逻辑 -> send。

方案

单Reactor单线程模型

image.png

组成

  • Reactor 对象的作用是监听和分发事件;
  • Acceptor 对象的作用是获取连接;
  • Handler 对象的作用是处理业务;

流程

  • Reactor 对象通过 select 函数监听事件,收到事件后通过 dispatch 进行分发
  • 如果是建立连接事件,则交由 Acceptor 对象进行处理,Acceptor 对象会通过 accept 方法 获取连接,并创建一个 Handler 对象来处理后续的响应事件;
  • 如果不是连接建立事件, 则交由当前连接对应的 Handler 对象来进行响应;
  • Handler 对象通过 read -> 业务处理 -> send 的流程来完成完整的业务流程。

优点

  • 因为只有一个线程,无法充分利用 多核 CPU 的性能
  • Handler 对象在业务处理时,整个线程是无法处理其他连接的事件的,如果业务处理耗时比较长,那么就造成响应的延迟

适用场景

  • 不适用计算机密集型的场景,只适用于业务处理非常快速的场景。
  • Redis采用该模型,因为 Redis 业务处理主要是在内存中完成,操作的速度是很快的

单Reactor多线程模型

image.png

流程

  • Reactor 对象通过 select 函数监听事件,收到事件后通过 dispatch 进行分发
  • 如果是建立连接事件,则交由 Acceptor 对象进行处理,Acceptor 对象会通过 accept 方法 获取连接,并创建一个 Handler 对象来处理后续的响应事件;
  • 如果不是连接建立事件, 则交由当前连接对应的 Handler 对象来进行响应;
  • Handler 对象不再负责业务处理,只负责数据的read和send,具体的业务处理交给子线程里的 Processor 对象;

优点

  • 能够充分利用多核 CPU 的性能

缺点

  • 多线程竞争资源的问题
  • 单reactor容易在高并发下出现性能瓶颈

主从Reactor多线程模型

image.png

流程

  • 主线程中的 MainReactor 对象通过 select 监听连接建立事件,收到事件后通过 Acceptor 对象 获取连接,将新的连接分配给某个子线程;
  • 子线程中的 SubReactor 对象将 MainReactor 对象分配的连接加入 select 继续进行监听,并创建一个 Handler 用于处理连接的响应事件。
  • Handler 对象通过 read -> 业务处理 -> send 的流程来完成完整的业务流程。

优点

  • 主线程和子线程分工明确,主线程只负责接收新连接,子线程负责完成后续的业务处理。
  • 主线程和子线程的交互很简单,主线程只需要把新连接传给子线程,子线程无须返回数据,直接就可以在子线程将处理结果发送给客户端。

应用

  • Netty
  • Nginx

Proactor模型

前面提到的 Reactor 是非阻塞同步网络模式,而 Proactor 是异步网络模式。Reactor 可以理解为「来了事件操作系统通知应用进程,让应用进程来处理」,而 Proactor 可以理解为「来了事件操作系统来处理,处理完再通知应用进程」。

image.png

流程

  • Proactor Initiator 负责创建 Proactor 和 Handler 对象,并将 Proactor 和 Handler 都通过
  • Asynchronous Operation Processor 注册到内核;
  • Asynchronous Operation Processor 负责处理注册请求,并处理 I/O 操作;
  • Asynchronous Operation Processor 完成 I/O 操作后通知 Proactor;
  • Proactor 根据不同的事件类型回调不同的 Handler 进行业务处理;
  • Handler 完成业务处理;

可惜的是,在 Linux 下的异步 I/O 是不完善的,
aio 系列函数是由 POSIX 定义的异步操作接口,不是真正的操作系统级别支持的,而是在用户空间模拟出来的异步,并且仅仅支持基于本地文件的 aio 异步操作,网络编程中的 socket 是不支持的,这也使得基于 Linux 的高性能网络程序都是使用 Reactor 方案。

而 Windows 里实现了一套完整的支持 socket 的异步编程接口,这套接口就是 IOCP,是由操作系统级别实现的异步 I/O,真正意义上异步 I/O,因此在 Windows 里实现高性能网络程序可以使用效率更高的 Proactor 方案。