程序设计中的两大经典模式 — Reactor & Proactor
1. 引言
Reactor 与 Proactor 模型是近几年技术领域频频提到的两个设计模式,那么,究竟什么是 Reator,什么又是 Proactor,他们之间有什么异同呢?
本文就来详细介绍一下。
2. UNIX 下的五种 IO 模型
此前,我们已经介绍过 linux 系统中的五种 IO 模型:
IO复用 & UNIX下的五种IO模型
在 IO 模型中,IO 复用模型,例如 epoll、select 等就是在 Reator 思想下诞生的,而异步 IO 模型,例如 glibc 实现的 posix aio 或是 linux 原生的 libaio 就是在 Proactor 思想下诞生的。
如果你非常熟悉 IO 复用模型与异步 IO 模型之间的差异,那么,关于 Reactor 与 Proactor 思想的区别就非常清晰了。
3. Reactor 模式
3.1. 模式构成
Reactor包含以下角色:
-
Handle 句柄 — 在 linux 中,就是常见的文件描述符,用来标识 socket 连接或是打开的文件
-
Reactor — 反应器,定义抽象接口,实现:
-
供应用程序注册和删除关注的事件句柄
-
运行事件循环
-
有就绪事件到来时,分发事件到之前注册的回调函数上处理
Synchronous Event Dispatcher — 同步事件多路分发器,由操作系统内核实现,用于阻塞等待发生在句柄上的一个或多个事件,我们系统中的 select、poll、epoll 等多路复用 IO 就充当了这一角色。
3. Event Handler — 事件处理接口。
3.2. 工作时序
下面展示了整个 Reactor 模式工作的时序:
整体的思想分为以下几步:
-
初始化启动应用,将事件注册到 Reactor 中
-
调用 get_handle() 接口,获取事件处理对象
-
调用 Reactor 进入事件循环,等待注册的事件到来
-
注册的事件触发,select() 返回,Reactor 回调已注册的回调函数
这一思想就是基于经典的回调思想“不要调用我,让我来调用你”的“好莱坞法则”设计的,具体的执行过程可以参看 epoll 的使用
4. Proactor 模式
Proactor 模式是另一个消息异步通知的设计模式,与 Reactor 的最大区别在于,Proactor 通知的不是就绪事件,而是操作完成事件,这也就是操作系统异步 IO 的主要模型。
4.1. 模式构成
Proactor 模式包含以下角色:
-
Handle 句柄 — 在 linux 中,就是常见的文件描述符,用来标识 socket 连接或是打开的文件
-
Asynchronous Operation Processor — 异步操作处理器;负责执行异步操作,一般由操作系统内核实现,也可以被用户态线程或进程模拟
-
Asynchronous Operation — 异步操作
-
Completion Event Queue — 完成事件队列,用来缓存已经完成的异步操作
-
Proactor — 主动器,定义抽象接口,实现:
-
为应用程序进程提供事件循环
-
从完成事件队列中取出异步操作的结果
-
分发调用已完成时间相应的后续处理逻辑
-
Completion Handler — 完成事件接口,一般是由回调函数组成的接口。
-
Concrete Completion Handler — 完成事件后的具体处理逻辑,实现接口定义特定的应用处理逻辑。
4.2. 模式执行时序
下图展现了 Proactor 执行的时序:
主要分为以下几步:
-
初始化启动,注册异步操作完成后的回调操作
-
主程序调用异步操作处理器提供的异步操作接口
-
Asynchronous Operation Processor 执行异步操作,完成后将结果放入事件完成队列
-
Proactor 从完成事件队列中取出结果,分发到相应的完成事件回调函数处理逻辑中
5. 优势与不足
5.1. 主动与被动 — Reactor 与 Proactor 的区别
Reactor 调用后,需要被动等待对象进入就绪状态,然后再进行后续处理。
Proactor 则会待操作完全完成后由内核返回,主进程可以主动切换去执行其他任务。
5.2. Reactor 的优势与不足
5.2.1. 优势
Reactor 在实现上相对比较简单,对于大量对象,频繁从非就绪态触发到就绪态的场景处理十分高效。
同时,操作系统可以同时去等待多个对象触发,并且可以在事件触发后自由地选择后续执行流程,具有很高的灵活性。
虽然并发编程实现阻塞式同步 IO 也可以实现同时等待多个对象触发的效果,但在编程的复杂度与资源的消耗等方面,Reactor 模式拥有明显的优势。
5.2.2. 不足
但是 Reactor 的不足也很明显,如果就绪态长时间没有触发,则进程一直等待,长时间阻塞主进程,影响到整个系统的吞吐。
5.3. Proactor 的优势与不足
此前我们介绍了 glibc 实现的 POSIX aio 与 linux 原生实现的 libaio,他们是典型的 Proactor 模式的处理模型:
POSIX AIO — glibc 版本异步 IO 简介
linux AIO — libaio 实现的异步 IO 简介及实现原理
5.3.1. 优势
Proactor 最显著的优势在于处理耗时长的 IO 操作和并发场景。
同时,针对 IO 操作,一旦提交,内核只有在完全执行完成后才会再次通知到用户进程,在这个过程中,用户进程可以做任何其他操作,这给与了用户进程更大的灵活性。
5.3.2. 不足
Proactor 的实现相对比较复杂,在实际编程中,与基本的同步 IO 相比,aio 在使用上也不那么容易,尤其是 linux 的 libaio 具有五个 api,同时需要自己构造执行上下文和 buffer,性能与 windows 下的 IOCP 相比也有一定的差距,普通场景中还是不建议使用 linux 的 aio 的。
6. 微信公众号
欢迎关注微信公众号,以技术为主,涉及历史、人文等多领域的学习与感悟,每周三到七篇推文,只有全部原创,只有干货没有鸡汤。
7. 参考资料
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。
《程序设计中的两大经典模式 — Reactor & Proactor》来自互联网,仅为收藏学习,如侵权请联系删除。本文URL:http://www.bookhoes.com/806.html