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包含以下角色:
3. Event Handler — 事件处理接口。
下面展示了整个 Reactor 模式工作的时序:
整体的思想分为以下几步:
这一思想就是基于经典的回调思想“不要调用我,让我来调用你”的“好莱坞法则”设计的,具体的执行过程可以参看 epoll 的使用
Proactor 模式是另一个消息异步通知的设计模式,与 Reactor 的最大区别在于,Proactor 通知的不是就绪事件,而是操作完成事件,这也就是操作系统异步 IO 的主要模型。
Proactor 模式包含以下角色:
下图展现了 Proactor 执行的时序:
主要分为以下几步:
Reactor 调用后,需要被动等待对象进入就绪状态,然后再进行后续处理。 Proactor 则会待操作完全完成后由内核返回,主进程可以主动切换去执行其他任务。
Reactor 在实现上相对比较简单,对于大量对象,频繁从非就绪态触发到就绪态的场景处理十分高效。 同时,操作系统可以同时去等待多个对象触发,并且可以在事件触发后自由地选择后续执行流程,具有很高的灵活性。 虽然并发编程实现阻塞式同步 IO 也可以实现同时等待多个对象触发的效果,但在编程的复杂度与资源的消耗等方面,Reactor 模式拥有明显的优势。
但是 Reactor 的不足也很明显,如果就绪态长时间没有触发,则进程一直等待,长时间阻塞主进程,影响到整个系统的吞吐。
此前我们介绍了 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。