🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] ## 概述 在介绍这两种模型之前先介绍一下在I/O场景下同步、异步、阻塞、非阻塞的概念。 我们都知道我们的程序是运行在操作系统上的,我们程序和服务器硬件之间隔着个操作系统,一般情况下我们的服务器都是linux系统,为了安全考虑linux系统又分了:**用户态和内核态**。 I/O操作得经历两个过程: 1、读存储设备数据到内核缓存 2、从内核缓存读数据到用户空间 >1操作比2操作慢的多,因为去磁盘寻址啊等操作比较慢。 然后我们**平日里针对I/O场景下说的阻塞I/O、非阻塞I/O指的就是1操作是否阻塞**,也就是会立即返回一个状态值,还是会等待存储设备数据读取到内核缓存后在返回所需的数据。 而平日里说的**同步I/O、异步I/O指的是2操作是否会阻塞。** 我们提到不同的操作系统实现的io策略可能不一样,即使是同一个操作系统也可能存在多重io策略,常见如linux上的select,poll,epoll,面对这么多不同类型的io接口,这里需要一层抽象api来完成,所以就演变出来两种高性能的io的设计模式,分别是Reactor(同步IO)和Proactor(异步IO)。 一般情况下,I/O 复用机制需要事件分享器(event demultiplexor)。 事件分离器的作用,即将那些读写事件源分发给各读写事件的处理者,就像送快递的小哥,拉着一三轮车快递停到了小区的快递收发区,然后打电话通知,谁谁谁的快递到了快来拿吧;谁谁谁要邮寄出去的快递,快来这里邮寄填写表格。这里面拿快递就类似IO的读请求,发快递就类似IO的写请求,而快递小哥则是事件分享器,并负责完成送和收快递的两种事件。开发人员在开始的时候需要在分享器那里注册感兴趣的事件,并提供相应的处理者(event handlers),或者是回调函数; 事件分享器在适当的时候会将请求的事件分发给这些handler或者回调函数。 ## 关于Reactor Reactor英文意思为反应器,类似于核能的反应堆一样,所有的能量都源源不断从这里传出.或者更贴切一点叫事件的分发器。在Reactor中,事件分离器负责等待文件描述符或socket为读写操作准备就绪,然后将就绪事件传递给对应的处理器,最后由处理器负责完成实际的读写工作。 Linux epoll 使用 Reactor 模式。Reactor 模式使用同步 I/O(一般来说)。Reactor 的标准(典型)的工作方式是: (1)应用程序注册读就绪事件和相关联的事件处理器 (2)Reactor阻塞等待内核事件通知 (3)Reactor收到通知,然后分发可读写事件(读写准备就绪)到用户事件处理函数 (4)用户读取数据,并处理数据 (5)事件处理器完成实际的读操作,处理读到的数据,注册新的事件,然后返还控制权。 这里面需要注意,事件分离器仅仅发现当有io事件都写就绪的时候,会通知用户线程来读取数据,这一步相当于io阻塞的第二个阶段,从内核空间拷贝数据到用户空间是由用户线程完成的,所以Reactor模式实际上还属于同步IO的模式,当然为了架构更灵活和性能更好,一般情况下事件分离器和实际的处理器线程是分开的,类似Netty里面的boss线程组合worker线程组一样。 ## 关于Proactor Proactor英文意思前摄器,是一种异步的IO设计模式。这种模式更加理想,但真正支持纯异步的io模式,目前只有windows的Windows IO completion port.(iocp)模型。 Windows iocp 使用 Proactor 模式。Proactor 模式使用异步 I/O(一般来说)。Proactor 的标准(典型)的工作方式是: (1)应用程序初始化一个异步读取操作,然后注册相应的事件处理器,此时事件处理器不关注读取就绪事件,而是关注读取完成事件,这是区别于Reactor的关键。 (2)事件分离器等待读取操作完成事件 (3)在事件分离器等待读取操作完成的时候,操作系统调用内核线程完成读取操作,并将读取的内容放入用户传递过来的缓存区中。这也是区别于Reactor的一点,Proactor中,应用程序需要传递缓存区。 (4)事件分离器捕获到读取完成事件后,激活应用程序注册的事件处理器,事件处理器直接从缓存区读取数据,而不需要进行实际的读取操作。 从上面的描述中能够看到,Proactor模式中,操作系统相当于直接把IO操作的两阶段工作都给干了,这也要求应用程序在注册异步任务时,需要传递一个缓存区,用来存放结果数据。这里面事件分离器关注的是io的完成事件,而不是就绪时间,当分离器通知应用程序时,应用程序可以直接就能处理数据了。 ## 总结 关于Reactor和Proactor这两种IO设计模式,我们举个实际生活中的例子:Reactor模式就是快递员在楼下,给你打电话告诉你快递到了,你需要自己下楼来拿快递。而在Proactor模式下,快递员直接将快递送到家里面的指定位置。 Reactor和Proactor是两种高性能的IO设计模式,分别用于同步IO和异步IO的策略,可以看出它们都是采用的IO复用的模式,都是对某个IO事件的事件通知(即告诉某个模块,这个IO操作可以进行或已经完成)。在结构上,两者也有相同点:demultiplexor负责提交IO操作(异步)、查询设备是否可操作(同步),然后当条件满足时,就回调handler; 不同点在于,异步情况下(Proactor),当回调handler时,表示IO操作已经完成(数据已从系统内核拷贝到程序内存);同步情况下(Reactor),回调handler时,表示IO设备可以进行某个操作(can read or can write,数据准备就绪,但是用户需要自己将数据从系统内核拷贝到程序内存)。