zoukankan      html  css  js  c++  java
  • Reactor模式及在DSS中的体现

      Reactor模式是处理并发I/O比较常见的一种模式,用于同步I/O中心思想是将所有要处理的I/O事件注册到一个中心I/O多路复用器上,同时主线程阻塞在多路复用器上;一旦有I/O事件到来或是准备就绪(区别在于多路复用器是边沿触发还是水平触发),多路复用器返回并将相应I/O事件分发到对应的处理器中。

      Reactor是一种事件驱动机制和普通函数调用的不同之处在于:应用程序不是主动的调用某个API完成处理,而是恰恰相反,Reactor逆置了事件处理流程,应用程序需要提供相应的接口并注册到Reactor上,如果相应的事件发生,Reactor将主动调用应用程序注册的接口,这些接口又称为“回调函数”。用“好莱坞原则”来形容Reactor再合适不过了:不要打电话给我们,我们会打电话通知你。

      Reactor模式与Observer模式在某些方面极为相似:当一个主体发生改变时,所有依属体都得到通知。不过,观察者模式与单个事件源关联,而反应器模式则与多个事件源关联 。 

    模式框架

     

    1 Handle 

      Handle代表操作系统管理的资源,包括:网络链接,打开的文件,计时器,同步对象等等。Linux上是文件描述符,Windows上就是Socket或者Handle了,这里统一称为“句柄集”;程序在指定的句柄上注册关心的事件,比如I/O事件。 

    2) Event Demultiplexer 

      事件分离器,由操作系统提供,在linux上一般是select, poll, epoll等系统调用,在一个Handle集合上等待事件的发生。接受client连接,建立对应client的事件处理器(Event Handler),并向事件分发器(Reactor)注册此事件处理器(Handler 

    3) Reactor(Initiation Dispatcher) 

      提供接口:注册,删除和派发Event Handler。Event Demultiplexer等待事件的发生,当检测到新的事件,就把事件交给Initiation Dispatcher,它去回调Event Handler。 

    4 Event Handler 

      事件处理器,负责处理特定事件的处理函数。一般在基本的Handler基础上还会有更进一步的层次划分,用来抽象诸如decode,process和encoder这些过程。比如对Web Server而言,decode通常是HTTP请求的解析,process的过程会进一步涉及到Listner和Servlet的调用。为了简化设计,Event Handler通常被设计成状态机,按GoF的state pattern来实现。 

    5Concrete Event Handler 

      继承上面的类,实现钩子方法。应用把Concrete Event Handler注册到Reactor,等待被处理的事件。当事件发生,这些方法被回调。 

    事件处理流程

    模式模型

    应用场景举例

    场景:长途客车在路途上,有人上车有人下车,但是乘客总是希望能够在客车上得到休息。

    传统做法:每隔一段时间(或每一个站),司机或售票员对每一个乘客询问是否下车。

    Reactor做法:汽车是乘客访问的主体(Reactor),乘客上车后,到售票员(acceptor)处登记,之后乘客便可以休息睡觉去了,当到达乘客所要到达的目的地后,售票员将其唤醒即可。

    1) 单线程模型

      这是最简单的单Reactor单线程模型。Reactor线程是个多面手,负责多路分离套接字,Accept新连接,并分派请求到处理器链中。该模型适用于处理器链中业务处理组件能快速完成的场景。不过这种单线程模型不能充分利用多核资源,所以实际使用的不多。 

    2) 多线程模型(单Reactor 

      相比上一种模型,该模型在事件处理器(Handler)链部分采用了多线程(线程池),也是后端程序常用的模型。 

     

    3) 多线程模型(多Reactor 

      这个模型比起第二种模型,它是将Reactor分成两部分,mainReactor负责监听accept新连接,然后将建立的socket通过多路复用器(Acceptor)分派给subReactorsubReactor负责多路分离已连接的socket,读写网络数据;业务处理功能,其交给worker线程池完成。通常subReactor个数上可与CPU个数等同。

      

    优缺点 

    优点 

    • 响应快,不必为单个同步时间所阻塞,虽然Reactor本身依然是同步的;

    • 编程相对简单,可以最大程度的避免复杂的多线程及同步问题,并且避免了多线程/进程的切换开销;

    • 可扩展性,可以方便的通过增加Reactor实例个数来充分利用CPU资源;

    • 可复用性,Reactor框架本身与具体事件处理逻辑无关,具有很高的复用性; 

    缺点 

    • 应用受限制:Reactor模式只能应用在支持Handle的操作系统上。虽然可以使用多线程模拟Reactor,但因为同步控制和上下文切换的要求,这种实现效率低,与Reactor模式出发点相违背。

    • 非抢占模式:在单线程的实现这种情况下,事件的处理必须不能使用阻塞的I/O,因此,如果存在长期操作,比如传输大量的数据。使用主动对象,效率可能更好,主动对象可以并发的处理这些任务。

    • 难以调试:使用Reactor模式的应用程序可能会难以调试,因为程序运行的控制流会在框架和应用相关的处理器之间跳转,不了解框架的应用程序开发人员难一跟着调试。 

    相关 

    ACE 

      ACE是一个大型的中间件产品,代码20万行左右,过于宏大,一堆的设计模式,架构了一层又一层,使用的时候,要根据情况,看从那一层来进行使用支持跨平台。 

    设计模式 :ACE主要应用了Reactor,Proactor等; 

    层次架构 :ACE底层是C风格的OS适配层,上一层基于C++wrap类,再上一层是一些框架 (Accpetor,Connector,Reactor,Proactor),最上一层是框架上服务 

    可移植性 :ACE支持多种平台,可移植性不存在问题,据说socket编程在linux下有不少bugs 

    事件分派处理 :ACE主要是注册handler类,当事件分派时,调用其handler的虚挂勾函数。实现 ACE_Handler/ACE_Svc_Handler/ACE_Event_handler等类的虚函数 

    涉及范围 :ACE包含了日志,IPC,线程池,共享内存,配置服务,递归锁,定时器等 

    线程调度 :ACEReactor是单线程调度,Proactor支持多线程调度 

    发布方式 :ACE是开源免费的,不依赖于第方库,一般应用使用它时,以动态链接的方式发布动态库开发难度 :基于ACE开发应用,对程序员要求比较高,要用好它,必须非常了解其框架。在其框架下开发,往往new出一个对象,不知在什么地方释放好。 

    Libevent 

      libevent是一个C语言写的网络库,官方主要支持的是类linux操作系统,最新的版本添加了对windowsIOCP的支持。在跨平台方面主要通过select模型来进行支持。 

    设计模式 :libeventReactor模式; 

    层次架构:livevent在不同的操作系统下,做了多路复用模型的抽象,可以选择使用不同的模型,通过事件函数提供服务 

    可移植性 :libevent主要支持linux平台,freebsd平台,其他平台下通过select模型进行支持,效率不是太高 

    事件分派处理 :libevent基于注册的事件回调函数来实现事件分发

    涉及范围 :libevent只提供了简单的网络API的封装,线程池,内存池,递归锁等均需要自己实现

    线程调度 :libevent的线程调度需要自己来注册不同的事件句柄

    发布方式 :libevent为开源免费的,一般编译为静态库进行使用

    开发难度 :基于libevent开发应用,相对容易,具体可以参考memcached这个开源的应用,里面使用了 libevent这个库。 

    Libev

      与 libevent 一样,libev 系统也是基于事件循环的系统,它在 poll()select() 等机制的本机实现的基础上提供基于事件的循环。libev 实现的开销更低,能够实现更好的基准测试结果。 

    Reactor模式在DSS中的体现

      Darwin流媒体服务器是由父进程及其fork出来的子进程构成的,子进程就是核心服务器。父进程的职责就是等待子进程退出。如果子进程出错退出,则父进程就会fork一个新的子进程,从而保证视频服务器继续提供服务。核心服务器的作用是充当VOD(视频点播)客户端与服务器模块之间的接口,VOD客户端采用RTPRTSP协议向服务器发送请求并接收响应,服务器模块负责处理VOD客户端的请求并向VOD客户端发送数据包。

      在DSS中,除主线程以外,还有有三种类型的线程

    • TaskThreadTaskThread通过运行Task类型对象的Run方法来完成相应Task的处理。典型的Task类型是RTSPSessionRTPSessionTaskThread的个数是可配置的,缺省情况下TaskThread的个数与处理器的个数一致。等待被TaskThread调用并运行的Task放在队列或者堆中。

    • EventThreadEventThread负责侦听套接口事件,在DSS中,有两种被侦听的事件,分别是建立RTSP连接请求的到达和RTSP请求的到达。对于RTSP连接请求的事件,EventThread建立一个RTSP
      Session
      ,并启动针对相应的socket的侦听。对于RTSP请求的事件,EventThread把对应的RTSPSession类型的Task加入到TaskThread的队列中,等待RTSP请求被处理。

    • IdleTaskThreadIdleTaskThread管理IdleTask类型对象的队列,根据预先设定的定时器触发IdleTask的调度。TCPListenerSocket就是一个IdleTask的派生类,当并发连接数达到设定的最大值时,会把派生自TCPListenerSocketRTSPListenerSocket加入到IdleTaskThread管理的IdleTask队列中,暂时停止对RTSP端口的侦听,直到被设定好的定时器触发。

    下图是Darwin Streaming Server核心架构的示意图。在这个示意图中有三种类型的要素,分别是线程,Task队列或者堆,被侦听的事件。 

      其中,事件线程Event threadEvent Demultiplexer事件分离,任务线程Task threadsEvent Handler事件处理器)

     

    这里的主线程(Main thread)就是Reactor模式中的Reactor(Initiation Dispatcher)

  • E-Mail : Mike_Zhang@live.com
  • 转载请注明出处,谢谢!
查看全文
  • 相关阅读:
    网络测量中基于Sketch方法的简单介绍
    Reading SBAR SDN flow-Based monitoring and Application Recognition
    Reading Meticulous Measurement of Control Packets in SDN
    Reading SketchVisor Robust Network Measurement for Sofeware Packet Processing
    ovs加dpdk在日志中查看更多运行细节的方法
    后缀数组
    (转载)LCA问题的Tarjan算法
    Codeforces Intel Code Challenge Final Round (Div. 1 + Div. 2, Combined) A. Checking the Calendar(水题)
    Vijos 1816统计数字(计数排序)
    卡特兰数
  • 原文地址:https://www.cnblogs.com/MikeZhang/p/ReactorPattern20120815.html
  • Copyright © 2011-2022 走看看