图解 Reactor 和 Proactor

Reactor

Reactor 和 Proactor 是两个高性能网络模式。

Reactor 模式也叫 Dispatcher 模式,即 I/O 多路复用监听事件,收到事件后,根据事件类型分配(Dispatch)给某个进程/线程。

Reactor 模式主要由 Reactor 和处理资源池这两个核心部分组成,它两负责的事情如下:

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

Reactor 模式是灵活多变的,可以应对不同的业务场景,灵活在于:

  • Reactor 的数量可以只有一个,也可以有多个
  • 处理资源池可以是单线程/进程,也可以是多线程

将上面的两个因素排列组设一下,理论上就可以有 4 种方案选择:

  • 单 Reactor 单进程 / 线程;
  • 单 Reactor 多进程 / 线程;
  • 多 Reactor 单进程 / 线程;
  • 多 Reactor 多进程 / 线程;

其中,「多 Reactor 单进程 / 线程」实现方案相比「单 Reactor 单进程 / 线程」方案,不仅复杂而且也没有性能优势,因此实际中并没有应用。剩下的 3 个方案都是比较经典的,且都有应用在实际的项目中。

单 Reactor 单进程/线程

示意图如下:

image-20220327143145926

可以看到进程里有三个对象:

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

该方案的缺点:

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

所有,单 Reactor 单进程的方案不适合计算机密集型的场景,只适用于业务处理非常快速的场景,比如 Redis 就采用了这种方案。

单 Reactor 多线程/多进程

示意图如下:

image-20220327143152265

与上一种方案不同的是:

  • Handler 对象不再负责业务处理,只负责数据的接收和发送,Handler 对象通过read读取到数据后,会将数据发送给子线程里的 Processor对象进行业务处理
  • 子线程里的Processor 对象就进行业务处理,处理完毕后,将结果发给主线程中的 Handler 对象,接着由 Handler 通过 send 方法将响应结果发送给 client。

该模型的优势:

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

缺点:

  • 多个线程就会涉及共享数据的竞争,要避免竞争导致数据错乱,需要加互斥锁
  • 因为一个 Reactor 对象承担所有事件的监听和响应,而且只在主线程中运行,在面对瞬间高并发的场景时,容易成为性能的瓶颈的地方。

多 Reactor 多线程/多进程

示意图如下:

image-20220327143200178

说明如下:

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

大名鼎鼎的两个开源软件 Netty 和 Memcache 都采用了「多 Reactor 多线程」的方案。

Proactor

Reactor 是非阻塞同步网络模型,而 Proactor 是异步网络模型

  • Reactor 是非阻塞同步网络模式,感觉的是就绪可读写事件。在每次感知到有事件发生(比如可读就绪事件)后,就需要应用进程主动调用 read 方法来完成数据的读取,也就是要应用进程主动将 socket 接收缓存中的数据读到应用进程内存中,这个过程是同步的,读取完数据后应用进程才能处理数据。

  • Proactor 是异步网络模式, 感知的是已完成的读写事件。在发起异步读写请求时,需要传入数据缓冲区的地址(用来存放结果数据)等信息,这样系统内核才可以自动帮我们把数据的读写工作完成,这里的读写工作全程由操作系统来做,并不需要像 Reactor 那样还需要应用进程主动发起 read/write 来读写数据,操作系统完成读写工作后,就会通知应用进程直接处理数据。

因此,Reactor 可以理解为「来了事件操作系统通知应用进程,让应用进程来处理」,而 Proactor 可以理解为「来了事件操作系统来处理,处理完再通知应用进程」

Reactor 模式是基于「待完成」的 I/O 事件,而 Proactor 模式则是基于「已完成」的 I/O 事件

看一下Proactor 模式示意图:

image-20220327143207959

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

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

巨人的肩膀:

https://mp.weixin.qq.com/s?__biz=MzUxODAzNDg4NQ==&mid=2247492645&idx=1&sn=9a574559b7a2f443a48d6926e941f5e5&chksm=f98da08fcefa29996e8c51a748bcf6dd1fc44aa3eeb4c72321567d26d03dc3579f7df289579c&scene=178&cur_album_id=1408057986861416450#rd

posted @ 2021-10-17 16:58  Maple~  阅读(154)  评论(0编辑  收藏  举报