Reactor模式是处理并发I/O比较常见的一种模式,用于同步I/O,中心思想是将所有要处理的I/O事件注册到一个中心I/O多路复用器上,同时主线程阻塞在多路复用器上;一旦有I/O事件到来或是准备就绪(区别在于多路复用器是边沿触发还是水平触发),多路复用器返回并将相应I/O事件分发到对应的处理器中。
Reactor是一种事件驱动机制,和普通函数调用的不同之处在于:应用程序不是主动的调用某个API完成处理,而是恰恰相反,Reactor逆置了事件处理流程,应用程序需要提供相应的接口并注册到Reactor上,如果相应的事件发生,Reactor将主动调用应用程序注册的接口,这些接口又称为“回调函数”。
模式框架
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来实现。
5) Concrete Event Handler
继承上面的类,实现钩子方法。应用把Concrete Event Handler注册到Reactor,等待被处理的事件。当事件发生,这些方法被回调。
常见模型
1) 单线程模型
这是最简单的单Reactor单线程模型。Reactor线程是个多面手,负责多路分离套接字,Accept新连接,并分派请求到处理器链中。该模型适用于处理器链中业务处理组件能快速完成的场景。不过这种单线程模型不能充分利用多核资源,所以实际使用的不多。
2) 多线程模型(单Reactor)
相比上一种模型,该模型在事件处理器(Handler)链部分采用了多线程(线程池),也是后端程序常用的模型。
3) 多线程模型(多Reactor)
这个模型比起第二种模型,它是将Reactor分成两部分,mainReactor负责监听并accept新连接,然后将建立的socket通过多路复用器(Acceptor)分派给subReactor。subReactor负责多路分离已连接的socket,读写网络数据;业务处理功能,其交给worker线程池完成。通常,subReactor个数上可与CPU个数等同。
优缺点
优点
-
响应快,不必为单个同步时间所阻塞,虽然Reactor本身依然是同步的;
-
编程相对简单,可以最大程度的避免复杂的多线程及同步问题,并且避免了多线程/进程的切换开销;
-
可扩展性,可以方便的通过增加Reactor实例个数来充分利用CPU资源;
-
可复用性,Reactor框架本身与具体事件处理逻辑无关,具有很高的复用性;
缺点
-
应用受限制:Reactor模式只能应用在支持Handle的操作系统上。虽然可以使用多线程模拟Reactor,但因为同步控制和上下文切换的要求,这种实现效率低,与Reactor模式出发点相违背。
-
非抢占模式:在单线程的实现这种情况下,事件的处理必须不能使用阻塞的I/O,因此,如果存在长期操作,比如传输大量的数据。使用主动对象,效率可能更好,主动对象可以并发的处理这些任务。
-
难以调试:使用Reactor模式的应用程序可能会难以调试,因为程序运行的控制流会在框架和应用相关的处理器之间跳转,不了解框架的应用程序开发人员难一跟着调试。
转自:http://www.cnblogs.com/MikeZhang/archive/2012/08/16/ReactorPattern20120815.html