高性能IO设计中的Reactor模式与Proactor模式

在高性能的IO设计中,有两个比较著名的模式ReactorProactor模式,其中Reactor模式用于同步I/O,而Proactor运用于异步I/O操做。在比较这两个模式以前,咱们首先要搞明白几个概念。什么是阻塞和非阻塞?什么是同步和异步?异步是针对应用程序内核的交互而言的,同步指的是用户进程触发IO操做并等待或者轮询的去查看IO操做是否就绪,而异步是指用户进程触发IO操做之后便开始作本身的事情,而当IO操做已经完成的时候会获得IO完成的通知(异步的特色就是通知)。而阻塞非阻塞是针对于进程在访问数据的时候,根据IO操做的就绪状态来采起的不一样方式,说白了是一种读取或者写入操做函数的实现方式,阻塞方式下读取或者写入函数将一直等待,而非阻塞方式下,读取或者写入函数会当即返回一个状态值。 通常来讲IO模型能够分为:同步阻塞同步非阻塞异步阻塞异步非阻塞java

同步阻塞IO缓存

在此种方式下,用户进程在发起一个IO操做之后,必须等待IO操做的完成,只有当真正完成了IO操做之后,用户进程才能运行。java传统的IO模型属于此种方式!并发

同步非阻塞IO异步

在此种方式下,用户进程发起一个IO操做之后可返回作其它事情,可是用户进程须要时不时的询问IO操做是否就绪,这就要求用户进程不停的去询问,从而引入没必要要的CPU资源浪费。目前Java的NIO就属于同步非阻塞IO。函数

异步阻塞IO性能

此种方式下是指应用发起一个IO操做之后,不等待内核IO操做的完成,等内核完成IO操做之后会通知应用程序,这其实就是同步和异步最关键的区别,同步必须等待或者主动的去询问IO是否完成,那么为何说是阻塞的呢?由于此时是经过select系统调用来完成的,而select函数自己的实现方式是阻塞的,而采用select函数有个好处就是它能够同时监听多个文件句柄(若是从UNP的角度看,select属于同步操做。由于select以后,进程还须要读写数据),从而提升系统的并发性!
spa

异步非阻塞IO操作系统

在此种模式下,用户进程只须要发起一个IO操做而后当即返回,等IO操做真正的完成之后,应用程序会获得IO操做完成的通知,此时用户进程只须要对数据进行处理就行了,不须要进行实际的IO读写操做,由于真正的IO读取或者写入操做已经由内核完成了。线程


搞清楚了以上概念之后,咱们再回过头来看看,Reactor模式和Proactor模式。(其实阻塞与非阻塞均可以理解为同步范畴下才有的概念,对于异步,就不会再去分阻塞非阻塞。对于用户进程,接到异步通知后,就直接操做进程用户空间里的数据好了)。首先来看看Reactor模式,Reactor模式应用于同步IO的场景。咱们分别以读操做和写操做为例来看看Reactor中的具体步骤。设计

读取操做

  • 应用程序注册读就绪事件和相关联的事件处理器

  • 事件分离器等待事件的发生

  • 当发生读就绪事件的时候,事件分离器调用第一步注册的事件处理器

  • 事件处理器首先执行实际的读取操做,而后根据读取到的内容进行进一步的处理

写操做

写入操做相似于读取操做,只不过第一步注册的是写就绪事件。

下面咱们来看看Proactor模式中读取操做和写入操做的过程。

读取操做

  • 应用程序初始化一个异步读取操做,而后注册相应的事件处理器,此时事件处理器不关注读取就绪事件,而是关注读取完成事件,这是区别于Reactor的关键。

  • 事件分离器等待读取操做完成事件

  • 在事件分离器等待读取操做完成的时候,操做系统调用内核线程完成读取操做(异步IO都是操做系统负责将数据读写到应用传递进来的缓冲区供应用程序操做,操做系统扮演了重要角色),并将读取的内容放入用户传递过来的缓存区中。这也是区别于Reactor的一点,Proactor中,应用程序须要传递缓存区。

  • 事件分离器捕获到读取完成事件后,激活应用程序注册的事件处理器,事件处理器直接从缓存区读取数据,而不须要进行实际的读取操做。

写操做:略。

Proactor中写入操做和读取操做,只不过感兴趣的事件是写入完成事件

从上面能够看出,Reactor和Proactor模式的主要区别就是真正的读取和写入操做是由谁来完成的,Reactor中须要应用程序本身读取或者写入数据,而Proactor模式中,应用程序不须要进行实际的读写过程,它只须要从缓存区读取或者写入便可,操做系统会读取缓存区或者写入缓存区到真正的IO设备

综上所述,同步和异步是相对于应用和内核的交互方式而言的,同步须要主动去询问,而异步的时候内核在IO事件发生的时候通知应用程序,而阻塞和非阻塞仅仅是系统在调用系统调用的时候函数的实现方式而已