Reactor and Proactor

[原文:http://blog.csdn.net/wenbingoon/article/details/9880365]

1 概述

IO读写时,多路复用机制都会依赖对一个事件多路分离器,负责把源事件的IO 事件分离出来,分别到相应的read/write事件分离器。涉及到事件分离器的两种模式分别就是 Reactor和Proactor,Reactor是基于同步IO的,Proactor是基于异步IO的。

在Reactor模式中,事件分离者等待某个事件或者可应用或个操作的状态发生(比如文件描述符可读写,或者是socket可读写),事件分离者就把这个事件传给事先注册的事件处理函数或者回调函数,由后者来做实际的读写操作。Reactor模式主要是提高系统的吞吐量,理解反应器模式的例子:Reactor模式,或者叫反应器模式

在Proactor模式中,事件处理者(或者代由事件分离者发起)直接发起一个异步读写操作(相当于请求),而实际的工作是由操作系统来完成的。发起时,需要提供的参数包括用于存放读到数据的缓存区,读的数据大小,或者用于存放外发数据的缓存区,以及这个请求完后的回调函数等信息。事件分离者得知了这个请求,它默默等待这个请求的完成,然后转发完成事件给相应的事件处理者或者回调。举例来说,在Windows上事件处理者投递了一个异步IO操作(称有 overlapped的技术),事件分离者等IOCompletion事件完成. 这种异步模式的典型实现是基于操作系统底层异步API的,所以我们可称之为“系统级别”的或者“真正意义上”的异步,因为具体的读写是由操作系统代劳的。

举个例子,将有助于理解Reactor与Proactor二者的差异,以读操作为例(类操作类似)。

在Reactor中实现读:

- 注册读就绪事件和相应的事件处理器

- 事件分离器等待事件

- 事件到来,激活分离器,分离器调用事件对应的处理器。

- 事件处理器完成实际的读操作,处理读到的数据,注册新的事件,然后返还控制权。

与如下Proactor(真异步)中的读过程比较:

- 处理器发起异步读操作(注意:操作系统必须支持异步IO)。在这种情况下,处理器无视IO就绪事件,它关注的是完成事件。

- 事件分离器等待操作完成事件

- 在分离器等待过程中,操作系统利用并行的内核线程执行实际的读操作,并将结果数据存入用户自定义缓冲区,最后通知事件分离器读操作完成。

- 事件分离器呼唤处理器。

- 事件处理器处理用户自定义缓冲区中的数据,然后启动一个新的异步操作,并将控制权返回事件分离器。

可以看出,两个模式的相同点,都是对某个IO事件的事件通知(即告诉某个模块,这个IO操作可以进行或已经完成)。在结构上,两者也有相同点:demultiplexor负责提交IO操作(异步)、查询设备是否可操作(同步),然后当条件满足时,就回调handler;

不同点在于,异步情况下(Proactor),当回调handler时,表示IO操作已经完成;同步情况下(Reactor),回调handler时,表示IO设备可以进行某个操作(can read or can write),handler这个时候开始提交操作。

2、Reactor模式

Reactor释义“反应堆”,是一种事件驱动机制。和普通函数调用的不同之处在于:应用程序不是主动的调用某个API完成处理,而是恰恰相反,Reactor逆置了事件处理流程,应用程序需要提供相应的接口并注册到Reactor上,如果相应的时间发生,Reactor将主动调用应用程序注册的接口,这些接口又称为“回调函数”。使用Libevent也是想Libevent框架注册相应的事件和回调函数;当这些时间发声时,Libevent会调用这些回调函数处理相应的事件(I/O读写、定时和信号)。
    用“好莱坞原则”来形容Reactor再合适不过了:不要打电话给我们,我们会打电话通知你。

3、两个模式简单对比

两个模式的相同点:(1)都是对某个IO事件的事件通知(即告诉某个模块,这个IO操作可以进行或已经完成)。(2)在结构上的相同点:demultiplexor负责提交IO操作(异步)、查询设备是否可操作(同步),然后当条件满足时,就回调handler。

不同点在于:异步情况下(Proactor),当回调handler时,表示IO操作已经完成;同步情况下(Reactor),回调handler时,表示IO设备可以进行某个操作(can read or can write),handler这个时候开始提交操作。

我的理解:两者的根本区别就在于《Unix网络编程第一卷:套接口API》第6章讲解的五种I/O模型,Proactor是基于异步I/O,Reactor是同步I/O(一般是I/O复用)。但是现在的操作系统并不是都能很好的真正支持异步I/O,比如Windows里有真正的异步I/O——IOCP,而Unix、Linux并没有真正实现异步I/O。所以考虑程序移植性以及现在很多服务器基于Unix,Linux;Proactor封装了这种差异,在内部异步事件分离器实现时根据系统的不同调用相应的I/O模式。

二、BIO、NIO、AIO

NIO通常采用Reactor模式,AIO通常采用Proactor模式。AIO简化了程序的编写,stream的读取和写入都有OS来完成,不需要像NIO那样子遍历Selector。Windows基于IOCP实现AIO,Linux只有eppoll模拟实现了AIO。

Java7之前的JDK只支持NIO和BIO,从7开始支持AIO。

4种通信方式:TCP/IP+BIO, TCP/IP+NIO, UDP/IP+BIO, UDP/IP+NIO。

TCP/IP+BIO、

Socket和ServerSocket实现,ServerSocket实现Server端端口监听,Socket用于建立网络IO连接。

不适用于处理多个请求 1.生成Socket会消耗过多的本地资源。2. Socket连接的建立一般比较慢。

BIO情况下,能支持的连接数有限,一般都采取accept获取Socket以后采用一个thread来处理,one connection one thread。无论连接是否有真正数据请求,都需要独占一个thread。

可以通过设立Socket池来一定程度上解决问题,但是使用池需要注意的问题是:1. 竞争等待比较多。 2. 需要控制好超时时间。

TCP/IP+NIO

使用Channel(SocketChannel和ServerSocketChannel)和Selector。

Server端通常由一个thread来监听connect事件,另外多个thread来监听读写事件。这样做的好处是这些连接只有在真是请求的时候才会创建thread来处理,one request one thread。这种方式在server端需要支持大量连接但这些连接同时发送请求的峰值不会很多的时候十分有效。

UDP/IP+BIO

DatagramSocket和DatagramPacket。DatagramSocket负责监听端口以及读写数据,DatagramPacket作为数据流对象进行传输。

UDP/IP是无连接的,无法进行双向通信,除非双方都成为UDP Server。

UDP/IP+NIO

通过DatagramChannel和ByteBuffer实现。DatagramChannel负责端口监听及读写。ByteBuffer负责数据流传输。

如果要将消息发送到多台机器,如果为每个目标机器都建立一个连接的话,会有很大的网络流量压力。这时候可以使用基于UDP/IP的Multicast协议传输,Java中可以通过MulticastSocket和DatagramPacket来实现。

Multicast一般多用于多台机器的状态同步,比如JGroups。SRM, URGCP都是Multicast的实现方式。eBay就采用SRM来实现将数据从主数据库同步到各个搜索节点机器。

------------------

就IO而言:概念上有5中模型:blocking I/O,nonblocking I/O,I/O multiplexing (select and poll),signal driven I/O (SIGIO),asynchronous I/O (the POSIX aio_functions)。

不同的操作系统对上述模型支持不同: unix支持io多路复用,不同系统叫法不同 :freebsd里面叫 kqueue;linux 是epoll。而windows: 2000的时候就诞生了IOCP支持最后一种异步I/O
java是一种跨平台语言,为了支持异步IO,诞生了nio,Java1.4引入的NIO 1.0是基于I/O复用的。在各个平台上会选择不同的复用方式。Linux用的epoll,BSD上用kqueue,Windows上应该是重叠I/O(肯定不是IOCP)。
 
NIO 2.0(Java1.7)里终于有AIO了,Linux上用AIO,Windows上用IOCP,都支持了概念上的最后一种IO -- asynchronous I/O

-------------------

[原文: http://blog.sina.com.cn/s/blog_9a97a37c0101aahl.html]

Reactor and Proactor两个模式的相同点,都是对某个IO事件的事件通知(即告诉某个模块,这个IO操作可以进行或已经完成)。在结构上,两者也有相同点:demultiplexor负责提交IO操作(异步)、查询设备是否可操作(同步),然后当条件满足时,就回调handler。

不同点在于,异步情况下(Proactor),当回调handler时,表示IO操作已经完成;同步情况下(Reactor),回调handler时,表示IO设备可以进行某个操作(can read or can write),handler这个时候开始提交操作。

用select模型写个简单的reactor,大致为:

///
class handler
{
public:
    virtual void onRead() = 0;
    virtual void onWrite() = 0;
    virtual void onAccept() = 0;
}; 

class dispatch
{
public:
    void poll()
    {
        // add fd in the set.
        //
        // poll every fd
        int c = select( 0, &read_fd, &write_fd, 0, 0 );
        if( c > 0 )
        {
            for each fd in the read_fd_set
            {    if fd can read
                    _handler->onRead();
                if fd can accept
                    _handler->onAccept();
            } 

            for each fd in the write_fd_set
            {
                if fd can write
                    _handler->onWrite();
            }
        }
    } 

    void setHandler( handler *_h )
    {
        _handler = _h;
    } 

private:
    handler *_handler;
}; 

/// application
class MyHandler : public handler
{
public:
    void onRead()
    {
    } 

    void onWrite()
    {
    } 

    void onAccept()
    {
    }
}; 

在网上找了份Proactor模式比较正式的文档,其给出了一个总体的UML类图,比较全面:

根据这份图我随便写了个例子代码:

class AsyIOProcessor
{
public:
    void do_read()
    {
        //send read operation to OS
        // read io finished.and dispatch notification
        _proactor->dispatch_read();
    } 

private:
    Proactor *_proactor;
}; 

class Proactor
{
public:
    void dispatch_read()
    {
        _handlerMgr->onRead();
    } 

private:
    HandlerManager *_handlerMgr;
}; 

class HandlerManager
{
public:
    typedef std::list<Handler*> HandlerList; 

public:
    void onRead()
    {
        // notify all the handlers.
        std::for_each( _handlers.begin(), _handlers.end(), onRead );
    } 

private:
    HandlerList *_handlers;
}; 

class Handler
{
public:
    virtual void onRead() = 0;
}; 

// application level handler.
class MyHandler : public Handler
{
public:
    void onRead() 
    {
        // 
    }
}; 

Reactor通过某种变形,可以将其改装为Proactor,在某些不支持异步IO的系统上,也可以隐藏底层的实现,利于编写跨平台
代码。我们只需要在dispatch(也就是demultiplexor)中封装同步IO操作的代码,在上层,用户提交自己的缓冲区到这一层,
这一层检查到设备可操作时,不像原来立即回调handler,而是开始IO操作,然后将操作结果放到用户缓冲区(读),然后再
回调handler。这样,对于上层handler而言,就像是proactor一样。

----

其他参考:

两种高性能I/O设计模式(Reactor/Proactor)的比较  http://blog.sina.com.cn/s/blog_5f435c130101ktl6.html

2种IO并发开发中的设计模式:Reactor and Proactor相关推荐

  1. I/O模型系列之四:两种高性能IO设计模式 Reactor 和 Proactor

    不同的操作系统实现的io策略可能不一样,即使是同一个操作系统也可能存在多重io策略,常见如linux上的select,poll,epoll,面对这么多不同类型的io接口,这里需要一层抽象api来完成, ...

  2. delay在java中有什么用_DelayQueue怎么在Java多线程并发开发中使用

    DelayQueue怎么在Java多线程并发开发中使用 发布时间:2020-12-05 17:29:31 来源:亿速云 阅读:56 作者:Leah 这篇文章给大家介绍DelayQueue怎么在Java ...

  3. 坦克大战游戏开发中的设计模式总结

    坦克大战游戏开发中的设计模式总结 github地址: https://github.com/2017403603/TankGame_Simple1.0.git https://github.com/2 ...

  4. 前端开发中常用设计模式-总结篇

    本文是向大家介绍前端开发中常用的设计模式,它使我们编写的代码更容易被复用,也更容易被人理解,并且保证代码的稳定可靠性. 1.什么是设计模式 通俗来讲,就是日常使用设计的一种惯性思维. 因为对应的这种思 ...

  5. 两种高性能 I/O 设计模式 Reactor 和 Proactor

    Reactor 和 Proactor 是基于事件驱动,在网络编程中经常用到两种设计模式. 曾经在一个项目中用到了网络库 libevent,也学习了一段时间,其内部实现所用到的就是 Reactor,所知 ...

  6. 高性能I/O设计模式Reactor和Proactor

    昨天购买了<程序员>杂志 2007.4期,第一时间去翻阅了一遍,其中有一篇<两种高性能I/O设计模式的比较>令人眼睛一亮,这是一篇译文,偶最近在一直想认真看看这方面的文章很久了 ...

  7. 开发中经常使用的5种设计模式

    提到设计模式,很多人都会觉得老生常谈,有些人觉得设计模式很有必要,有些人觉得设计模式没那么重要,那么我们在工作中是否应该重视设计模式呢?我们是否应该将设计模式大量应用到我们的生产过程中呢? 如果你从未 ...

  8. 浅谈在游戏陪玩开发中常见的几种加密算法及实现

    前言 数字签名.信息加密是游戏陪玩开发前后端都经常需要使用到的技术,应用场景包括了用户登入.交易.信息通讯.oauth 等等,不同的应用场景需要游戏陪玩开发时使用到不同的签名加密算法,或者需要搭配不一 ...

  9. Linux中5种IO模型

    在了解IO模型时需要清楚什么是同步和异步,什么是阻塞和非阻塞 同步/异步 阻塞/非阻塞 当IO操作发生时,一定是两方参与的,分别是调用方和被调用方.阻塞和非阻塞相对于的事调用方,同步和异步相对于的好似 ...

最新文章

  1. 关于element的select多选选择器,数据回显的问题
  2. 万字详解本地缓存之王 Caffeine
  3. 国务院办公厅关于2022年部分节假日安排的通知
  4. matlab复数方程组,【求解】matlab求解非齐次方程组,但是系数矩阵是复数,求帮忙...
  5. 计算机网络技术中的数据通信
  6. linux 内核地址随机化,GNU/Linux内核的地址随机化
  7. 步骤菜单使用css3实现
  8. ECS开放批量创建实例接口RunInstances
  9. 下载Python安装包及支持包路径
  10. cfa英语不好的怎么学_英语不好能考CFA吗?看看他是怎么做到的
  11. 多目标优化_学习笔记(三)MOEA/D
  12. Java毕业设计:人民医院体检预约系统(java+springboot+vue+mysql)
  13. 处理ArchLinux上各软件屏幕卡顿与显示问题(chrome浏览器,微信开发者工具wxdt,vscode移动慢问题)
  14. Google Authenticator 原理及Java实现
  15. 激光雷达的分类、主流激光雷达、原理是什么
  16. 递推递归练习 B - 王小二切饼
  17. Java实现 LeetCode 488 祖玛游戏
  18. 化纤与纺织技术杂志化纤与纺织技术杂志社化纤与纺织技术编辑部2022年第2期目录
  19. 关于js数组的常用方法的总结
  20. Lite-HRNet:轻量级HRNet,FLOPs大幅下降

热门文章

  1. vue项目多页面入口配置
  2. 用Python实现一个SVM分类器策略
  3. openstack-5:安装rabbitmq
  4. 深入Java集合学习系列:HashMap的实现原理
  5. Nagios监控系统详解
  6. html之marquee详解
  7. SSH Secure Shell显示GCC编译错误信息乱码解决方法
  8. 北京XXX学院WLAN项目现场勘查报告
  9. spring依赖注入_Spring源码阅读:Spring依赖注入容器
  10. python用代码安装3.6_Python3.6安装及引入Requests库的实现方法