reactor模式
参考资料
• 维基百科:https://en.wikipedia.org/wiki/Reactor_pattern
事件驱动模式
我们已经知道,当前对于一台主机,其CPU的处理速度是要远远高于IO的处理速度。如果为了一个IO操作(譬如从Socket收取一段数据),应用程序阻塞等待IO操作的完成是不划算的,这将白白浪费分配给进程的CPU时间。好一些的做法是分配多个进程或多个线程进行IO处理,但这样又会带来进程切换或线程切换的开销。考虑这样的情况,某个进程读取一段数据将花费500ms,在这期间切换到该进程3次,但此时什么都不能处理,白白浪费CPU时间。事件驱动模式,或者称为事件回调方式的提出,可以有效地解决这个问题。这种方式下,应用层业务向一个“中间人”注册一个事件回调服务(Event Handler),当IO就绪(可读或可写)时,该“中间人”抛出一个事件,并通知相应的事件回调函数进行处理。事件回调方式也正是“好莱坞原则”(Hollywood Principle : Don't call us, we'll call you)在软件开发中的一个体现。
其实事件驱动模式可以很容易在现实生活中找到对应的场景,以餐饮服务为例:每一个顾客前来就餐时将发出一个服务请求(Request),然后浏览菜单,最后进行点餐。这个过程中需要我们服务人员处理这些就餐请求。在多线程处理模式下,会是这样的过程:一个顾客开就餐,需要一个服务员前往服务,顾客浏览菜单,然后点菜,最后服务人员将菜单递交给后厨。当5个人同时落座就餐,就需要5个服务员同时前往服务。这就是多线程的处理思路,一个业务请求到达时就需要一个专门的线程进行处理。这种方式在人比较少的时候获得较好的用户体验,每个顾客都能及时得到服务。在这种服务态度下,这家店的口碑得到认可,于是前来吃饭的顾客多了起来,于是同一时间会来10个顾客就餐。餐厅老板很开心,但此时只有5个服务员,为了不降低服务品质,老板又聘请了5个服务员。越来越多的人对这家餐厅表示满意,顾客渐渐多了起来,同时前来吃饭的人到达了20,这时候老板犯愁了,如果再聘请10个服务员,势必会影响餐厅收入的利润;如果不聘请服务员,而某些正在接收服务员服务的顾客点菜很慢,这样势必会降低其他顾客的服务质量。老板后来观察发现,大多数客人点菜的过程是比较慢的,服务员的时间闲置在等待客人点菜的过程中,于是老板终于发现了一个新的方法:当客人浏览菜单的时候,服务员可以前往招呼其他客人,等客人浏览完菜单,招呼一声“服务员”,马上就会有个服务员前往服务。客人招呼服务员就是发起了一个事件(Event),服务员前往服务就是此事件的回调服务(Event Handler)。最终老板根据业务并发量留下了合适数量的服务员。
Reactor模式简介
系统I/O处理方式一般可分为阻塞、非阻塞同步和非阻塞异步三种。这三种方式中,非阻塞异步模式的扩展性和性能最好。非阻塞异步模式可以用多种IO多路复用机制实现,譬如Reactor模式和Proactor模式。在Reactor模式里,操作系统只负责通知IO就绪,而具体的IO操作仍然由业务进程中阻塞处理;Proactor模式则在此基础上更进一步,由操作系统将IO操作执行完毕,而事件处理过程只负责处理业务逻辑,做到了IO与程序处理异步执行。因此我们一般称Reactor模式是同步IO模式,而Proactor模式是异步IO模式。
一般I/O多路复用机制都依赖一个事件多路分离器(Event Demultiplexer)。事件多路分离器对象可将来自事件源的IO事件(Event)分离出来,并分发到对应的IO事件处理器(Event Handler)中。开发人员需要预先注册事件及其事件处理器(或回调函数)。两个与事件多路分离器有关的设计模式是上面提及的Reactor模式和Proactor模式。
Reactor模式目标是在应用中,基于事件驱动机制将一个或多个用户服务请求分离(Demultiplex)并调度(Dispatch)给应用程序,使得应用程序能同步有序地处理同时接收的多个服务请求。
Reactor模式详解
• UML结构图
• 结构说明
在Reactor模式中,有5个关键的参与者。
▶ 事件源 - Resource Handle
Wikipedia : Any resource that can provide input to or consume output from the system.
可以为系统提供输入或产生输出的任何资源。在Linux系统上体现是文件描述符(File Descriptor),在Windows系统上体现是网络套接字(Socket)或各种资源句柄(Handle),这里统一将事件源称为资源句柄(Resource Handle)。
▶ 同步事件分离器 - Synchronous Event Demultiplexer
Wikipedia : Uses an event loop to block on all resources. When it is possible to start a synchronous operation on a resource without blocking, the demultiplexer sends the resource to the dispatcher.
使用一个事件循环在资源句柄上阻塞等待。当某个资源有事件发生,这意味着能够立即在该资源上发起一个非阻塞的同步操作,同步事件分离器将派发该资源句柄到事件处理器中。这通常是利用操作系统提供的一些IO多路复用机制来实现,譬如select、poll和epoll等模型中的select函数、poll函数和epoll_wait函数就是常用的事件分离器。同步事件分离器通常需要管理资源句柄和事件的映射关系。
▶ 事件派发器 - Event Dispatcher or Request Dispatcher
Wikipedia : Handles registering and unregistering of request handlers. Dispatches resources from the demultiplexer to the associated request handler.
负责处理请求句柄的注册和注销的操作,并且在事件发生时将资源句柄从事件分离器发送到资源句柄绑定的事件处理器中。
▶ 事件处理器 - Event Handler or Request Handler
Wikipedia : An application defined request handler and its associated resource.
资源句柄相关联的事件处理函数。这通常包含抽象的事件处理器接口和具体的事件处理器实现,并且每个具体的事件处理器都将和某个资源句柄绑定在一起。事件处理器将拥有资源句柄。
▶ 反应器 - Reactor
Reactor是事件管理的接口集,它的主要责任是:在内部使用同步事件分离器进行事件注册和注销操作、运行事件循环、当有事件进入就绪状态时调用注册事件的回调函数。