程序设计中的两大经典模式 -- Reactor & Proactor

2020-06-12 23:46:57   最后更新: 2020-06-12 23:46:57   访问数量:89




Reactor 与 Proactor 模型是近几年技术领域频频提到的两个设计模式,那么,究竟什么是 Reator,什么又是 Proactor,他们之间有什么异同呢?

本文就来详细介绍一下

 

此前,我们已经介绍过 linux 系统中的五种 IO 模型:

IO复用 & UNIX下的五种IO模型

 

在 IO 模型中,IO 复用模型,例如 epoll、select 等就是在 Reator 思想下诞生的,而异步 IO 模型,例如 glibc 实现的 posix aio 或是 linux 原生的 libaio 就是在 Proactor 思想下诞生的

如果你非常熟悉 IO 复用模型与异步 IO 模型之间的差异,那么,关于 Reactor 与 Proactor 思想的区别就非常清晰了

 

模式构成

Reactor包含以下角色:

  1. Handle 句柄 -- 在 linux 中,就是常见的文件描述符,用来标识 socket 连接或是打开的文件
  2. Reactor -- 反应器,定义抽象接口,实现:
    1. 供应用程序注册和删除关注的事件句柄
    2. 运行事件循环
    3. 有就绪事件到来时,分发事件到之前注册的回调函数上处理

  3. Synchronous Event Dispatcher -- 同步事件多路分发器,由操作系统内核实现,用于阻塞等待发生在句柄上的一个或多个事件,我们系统中的 select、poll、epoll 等多路复用 IO 就充当了这一角色
  4. Event Handler -- 事件处理接口

 

 

 

工作时序

下面展示了整个 Reactor 模式工作的时序:

 

 

整体的思想分为以下几步:

  1. 初始化启动应用,将事件注册到 Reactor 中
  2. 调用 get_handle() 接口,获取事件处理对象
  3. 调用 Reactor 进入事件循环,等待注册的事件到来
  4. 注册的事件触发,select() 返回,Reactor 回调已注册的回调函数

 

这一思想就是基于经典的回调思想“不要调用我,让我来调用你”的“好莱坞法则”设计的,具体的执行过程可以参看 epoll 的使用:

epoll 的使用

 

Proactor 模式是另一个消息异步通知的设计模式,与 Reactor 的最大区别在于,Proactor 通知的不是就绪事件,而是操作完成事件,这也就是操作系统异步 IO 的主要模型

 

模式构成

Proactor 模式包含以下角色:

  1. Handle 句柄 -- 在 linux 中,就是常见的文件描述符,用来标识 socket 连接或是打开的文件
  2. Asynchronous Operation Processor -- 异步操作处理器;负责执行异步操作,一般由操作系统内核实现,也可以被用户态线程或进程模拟
  3. Asynchronous Operation -- 异步操作
  4. Completion Event Queue -- 完成事件队列,用来缓存已经完成的异步操作
  5. Proactor -- 主动器,定义抽象接口,实现:
    1. 为应用程序进程提供事件循环
    2. 从完成事件队列中取出异步操作的结果
    3. 分发调用已完成时间相应的后续处理逻辑

  6. Completion Handler -- 完成事件接口,一般是由回调函数组成的接口
  7. Concrete Completion Handler -- 完成事件后的具体处理逻辑,实现接口定义特定的应用处理逻辑

 

 

 

模式执行时序

下图展现了 Proactor 执行的时序:

 

 

主要分为以下几步:

  1. 初始化启动,注册异步操作完成后的回调操作
  2. 主程序调用异步操作处理器提供的异步操作接口
  3. Asynchronous Operation Processor 执行异步操作,完成后将结果放入事件完成队列
  4. Proactor 从完成事件队列中取出结果,分发到相应的完成事件回调函数处理逻辑中

 

主动与被动 -- Reactor 与 Proactor 的区别

Reactor 调用后,需要被动等待对象进入就绪状态,然后再进行后续处理

Proactor 则会待操作完全完成后由内核返回,主进程可以主动切换去执行其他任务

 

Reactor 的优势与不足

优势

Reactor 在实现上相对比较简单,对于大量对象,频繁从非就绪态触发到就绪态的场景处理十分高效

同时,操作系统可以同时去等待多个对象触发,并且可以在事件触发后自由地选择后续执行流程,具有很高的灵活性

虽然并发编程实现阻塞式同步 IO 也可以实现同时等待多个对象触发的效果,但在编程的复杂度与资源的消耗等方面,Reactor 模式拥有明显的优势

 

不足

但是 Reactor 的不足也很明显,如果就绪态长时间没有触发,则进程一直等待,长时间阻塞主进程,影响到整个系统的吞吐

 

Proactor 的优势与不足

此前我们介绍了 glibc 实现的 POSIX aio 与 linux 原生实现的 libaio,他们是典型的 Proactor 模式的处理模型:

POSIX AIO -- glibc 版本异步 IO 简介

linux AIO -- libaio 实现的异步 IO 简介及实现原理

 

优势

Proactor 最显著的优势在于处理耗时长的 IO 操作和并发场景

同时,针对 IO 操作,一旦提交,内核只有在完全执行完成后才会再次通知到用户进程,在这个过程中,用户进程可以做任何其他操作,这给与了用户进程更大的灵活性

 

不足

Proactor 的实现相对比较复杂,在实际编程中,与基本的同步 IO 相比,aio 在使用上也不那么容易,尤其是 linux 的 libaio 具有五个 api,同时需要自己构造执行上下文和 buffer,性能与 windows 下的 IOCP 相比也有一定的差距,普通场景中还是不建议使用 linux 的 aio 的

 

欢迎关注微信公众号,以技术为主,涉及历史、人文等多领域的学习与感悟,每周三到七篇推文,只有全部原创,只有干货没有鸡汤

 

 

http://www.dre.vanderbilt.edu/~schmidt/PDF/reactor-siemens.pdf

https://en.wikipedia.org/wiki/Reactor_pattern

https://en.wikipedia.org/wiki/Proactor_pattern

https://www.dre.vanderbilt.edu/~schmidt/PDF/Proactor.pdf

http://lse.sourceforge.net/io/aio.html

 






select      epoll      poll      io复用      aio      reactor      设计模式      procator     


京ICP备15018585号