参考资料
• 维基百科: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
? 同步事件分离器 - Synchronous Event Demultiplexer