专栏首页我是攻城师IO设计模式之Reactor和Proactor

IO设计模式之Reactor和Proactor

前言

上面文章中,我们提到不同的操作系统实现的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 使用 Praactor 模式。Praactor 模式使用异步 I/O(一般来说)。Praactor 的标准(典型)的工作方式是:

(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,数据准备就绪,但是用户需要自己将数据从系统内核拷贝到程序内存)。

本文分享自微信公众号 - 我是攻城师(woshigcs)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-11-24

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 10行Java代码实现最近被使用(LRU)缓存

    我是攻城师
  • 实时收集Storm日志到ELK集群

    我是攻城师
  • 如何在elasticsearch里面使用深度分页功能

    我是攻城师
  • 5G技术与人工智能可以帮您的爱车省多少油?

    随着5G牌照的发放,无人驾驶车辆也在加速产业化。那么,5G技术与人工智能可以帮您的爱车省多少油呢?

    思谱云汇
  • R语言实现基因详细信息的获取

    做生物信息学的同仁应该对基因的名称或者ID 的统一化对处理数据起到了很关键的作用。今天我们就给大家介绍一个R包TxDb.Hsapiens.UCSC.hg19.k...

    一粒沙
  • MySQL入门详解(二)---mysql事务、锁、以及优化

    1.脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据

    步履不停凡
  • eclipse远程调试命令行执行的maven运行程序

    版权声明:本文为博主原创文章,转载请注明源地址。 https://blog.csdn.net...

    用户1148648
  • RabbitMQ 学习笔记3 - 使用amqp库连接RabbitMQ

    使用Go 操作RabbitMQ 收发消息,可以 使用Go RabbitMQ客户端库 连接 RabbitMQ 来实现。

    zhangyunfeiVir
  • 在虚拟机上容器环境性能静态测试问题分析总结(一)

    最近在一个客户的项目拓展和做过程中,希望客户在IDC中自建的容器服务能够部分使用云上的容器服务,基于IDC环境和虚拟机上的容器服务之间,做了一些静态和动态的性能...

    张小波
  • 用磁铁为自动驾驶汽车导航将成真

    为了实现更安全的驾驶,汽车制造商自1980年代起不断地在汽车中整合进更多的智能化与仪控化功能。特别是自动驾驶汽车设计,几乎全部都得仰赖在汽车中整合足够的智能化,...

    机器人网

扫码关注云+社区

领取腾讯云代金券