服务器高效I/O设计模式
服务器高效I/O设计模式
满小茂 发表于2年前
服务器高效I/O设计模式
  • 发表于 2年前
  • 阅读 5871
  • 收藏 198
  • 点赞 13
  • 评论 15

【腾讯云】新注册用户域名抢购1元起>>>   

摘要: 两种I/O多路复用方案:Reactor和Proactor设计模式

前言

 

         一般地,I/O多路复用机制都依赖于一个事件多路分离器(Event Demultiplexer)。分离器对象可将来自事件源的I/O事件分离出来,并分发到对应的read/write事件处理器(Event Handler)。开发人员预先注册需要处理的事件及其事件处理器(或回调函数);事件分离器负责将请求事件传递给事件处理器。两个与事件分离器有关的模式是Reactor和Proactor。Reactor模式采用同步IO,而Proactor采用异步IO。

      在Reactor中,事件分离器负责等待文件描述符或socket为读写操作准备就绪,然后将就绪事件传递给对应的处理器,最后由处理器负责完成实际的读写工作。

      在Proactor模式中,处理器--或者兼任处理器的事件分离器,只负责发起异步读写操作。IO操作本身由操作系统来完成。传递给操作系统的参数需要包括用户定义的数据缓冲区地址         和 数据大小,操作系统才能从中得到写出操作所需数据,或写入从socket读到的数据。事件分离器捕获IO操作完成事件,然后将事件传递给对应处理器。

       比如,在windows上,处理器发起一个异步IO操作,再由事件分离器等待IOCompletion事件。典型的异步模式实现,都建立在操作系统支持异步API的基础之上,我们将这种实现称为“系统级”异步或“真”异步,因为应用程序完全依赖操作系统执行真正的IO工作。

 

解释

    Reactor反应器:

        “反应”即“倒置”,“控制逆转”,具体事件处理程序不调用反应器,而是由反应器分配一个具体事件处理程序,具体事件处理程序对某个指定的事件发生做出反应;这种控制逆转又称为“好莱坞法则”(不要调用我,让我来调用你)

    Proactor主动器:

         应用程序启动,调用异步操作处理器提供的异步操作接口函数,调用之后应用程序和异步操作处理就独立运行;应用程序可以调用新的异步操作,而其它操作可以并发进行; 应用程序启动Proactor主动器,进行无限的事件循环,等待完成事件到来; 异步操作处理器执行异步操作,完成后将结果放入到完成事件队列; 主动器从完成事件队列中取出结果,分发到相应的完成事件回调函数处理逻辑中;

 

两种模式的步骤

标准的经典的 Reactor模式

  • 步骤 1) 等待事件 (Reactor 的工作)
  • 步骤 2) 发”已经可读”事件发给事先注册的事件处理者或者回调 ( Reactor 要做的)

  • 步骤 3) 读数据 (用户代码要做的)

  • 步骤 4) 处理数据 (用户代码要做的)

Proactor模式

  • 步骤 1) 等待事件 (Proactor 的工作)

  • 步骤 2) 读数据(看,这里变成成了让 Proactor 做这个事情,可用用操作系统的异步io函数,也可以用一个主线程来模拟异步io)

  • 步骤 3) 把数据已经准备好的消息给用户处理函数,即事件处理者(Proactor 要做的)

  • 步骤 4) 处理数据 (用户代码要做的)

     

 

对比两者的区别

主动和被动

以主动写为例:
       Reactor将handle放到select(),等待可写就绪,然后调用write()写入数据;写完处理后续逻辑;
       Proactor调用aoi_write后立刻返回,由内核负责写操作,写完后调用相应的回调函数处理后续逻辑;

 可以看出,Reactor被动的等待指示事件的到来并做出反应;它有一个等待的过程,做什么都要先放入到监听事件集合中等待handler可用时再进行操作;

     Proactor直接调用异步读写操作,调用完后立刻返回;

实现

Reactor实现了一个被动的事件分离和分发模型,服务等待请求事件的到来,再通过不受间断的同步处理事件,从而做出反应;

Proactor实现了一个主动的事件分离和分发模型;这种设计允许多个任务并发的执行,从而提高吞吐量;并可执行耗时长的任务(各个任务间互不影响)

优点

Reactor实现相对简单,对于耗时短的处理场景处理高效;
       操作系统可以在多个事件源上等待,并且避免了多线程
编程相关的性能开销和编程复杂性;
       事件的串行化对应用是透明的,可以顺序的同步执行而不需要加锁;
       事务分离:将与应用无关的多路分解和分配机制和与应用相关的回调函数分离开来,

Proactor性能更高,能够处理耗时长的并发场景

缺点

Reactor处理耗时长的操作会造成事件分发的阻塞,影响到后续事件的处理;

Proactor实现逻辑复杂;依赖操作系统对异步的支持,目前实现了纯异步操作的操作系统少,实现优秀的如windows IOCP,但由于其windows系统用于服务器的局限性,目前应用范围 较小;而Unix/Linux系统对纯异步的支持有限,应用事件驱动的主流还是通过select/epoll来实现

(Linux有对aio的支持,可以参照这篇文章:http://www.lenky.info/archives/2013/01/2183  linux native AIO与eventfd、epoll的结合使用);

适用场景

Reactor:同时接收多个服务请求,并且依次同步的处理它们的事件驱动程序;

Proactor:异步接收和同时处理多个服务请求的事件驱动程序;

 

 

  • 打赏
  • 点赞
  • 收藏
  • 分享
共有 人打赏支持
粉丝 64
博文 111
码字总数 122333
评论 (15)
gx1727
学习
十一月不远
学习。。。。。
走起来
有些不明白, Proactor分发请求事件到工作线程, 这个工作线程是动态创建的么?
满小茂

引用来自“走起来”的评论

有些不明白, Proactor分发请求事件到工作线程, 这个工作线程是动态创建的么?
工作线程一般是程序初始化先创建好了的,实际开发中会把分发请求发到线程池去让线程池自己调度执行
走起来

引用来自“走起来”的评论

有些不明白, Proactor分发请求事件到工作线程, 这个工作线程是动态创建的么?

引用来自“满小茂”的评论

工作线程一般是程序初始化先创建好了的,实际开发中会把分发请求发到线程池去让线程池自己调度执行
这个线程池调度,是找空闲工作线程, 还是固定线程哈?
满小茂

引用来自“走起来”的评论

有些不明白, Proactor分发请求事件到工作线程, 这个工作线程是动态创建的么?

引用来自“满小茂”的评论

工作线程一般是程序初始化先创建好了的,实际开发中会把分发请求发到线程池去让线程池自己调度执行

引用来自“走起来”的评论

这个线程池调度,是找空闲工作线程, 还是固定线程哈?
这个看自己设计,一般是线程池自己找空闲线程,固定线程实现不太好
twisted3
这两种io模式又没有做性能上的对比
走起来

引用来自“走起来”的评论

有些不明白, Proactor分发请求事件到工作线程, 这个工作线程是动态创建的么?

引用来自“满小茂”的评论

工作线程一般是程序初始化先创建好了的,实际开发中会把分发请求发到线程池去让线程池自己调度执行

引用来自“走起来”的评论

这个线程池调度,是找空闲工作线程, 还是固定线程哈?

引用来自“满小茂”的评论

这个看自己设计,一般是线程池自己找空闲线程,固定线程实现不太好
谢谢大咖这么有耐心, 受益多多!
满小茂

引用来自“twist_fate”的评论

这两种io模式又没有做性能上的对比
性能上的对比就是优点缺点,这两种模式用的场景不一样,不能说谁好谁坏
航海家
支持前摄器模式。
感觉boost asio(前摄器),比libevent(反应堆)好用。
满小茂

引用来自“航海家”的评论

支持前摄器模式。
感觉boost asio(前摄器),比libevent(反应堆)好用。
各有所长,linux下的东西还是纯C的好
feiyu1993
mark
Pader
前面说了那么多,到最后说白了就是异步单线程和同步多线程的区别,我也是醉了。另外BSD的kevent也不说一下非常失望。
满小茂

引用来自“Pader”的评论

前面说了那么多,到最后说白了就是异步单线程和同步多线程的区别,我也是醉了。另外BSD的kevent也不说一下非常失望。

这是两种模式,主要区别在数据读取交给用户还是系统,至于你怎么实现基于什么平台是你的事,
qinshuluye
写的好
×
满小茂
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: