libevent 源码深度剖析 02¶
Reactor 模式
前面讲到,整个 libevent 本身就是一个 Reactor,因此本节将专门对 Reactor 模式进行必要的介绍,并列出 libevent 中的几个重要组件和 Reactor 的对应关系,在后面的章节中可能还会提到本节介绍的基本概念。
1 Reactor 的事件处理机制¶
首先来回想一下普通函数调用的机制:程序调用某函数?函数执行,程序等待?函数将结果和控制权返回给程序?程序继续处理。
Reactor 释义“反应堆”,是一种事件驱动机制。和普通函数调用的不同之处在于:应用程序不是主动的调用某个API完成处理,而是恰恰相反,Reactor 逆置了事件处理流程,应用程序需要提供相应的接口并注册到 Reactor 上,如果相应的时间发生,Reactor 将主动调用应用程序注册的接口,这些接口又称为“回调函数”。使用 libevent 也是想 libevent 框架注册相应的事件和回调函数;当这些事件发生时,libevent 会调用这些回调函数处理相应的事件(I/O 读写、定时和信号)。
用“好莱坞原则”来形容 Reactor 再合适不过了:不要打电话给我们,我们会打电话通知你。
举个例子:你去应聘某 xx 公司,面试结束后。
“普通函数调用机制”公司 HR 比较懒,不会记你的联系方式,那怎么办呢,你只能面试完后自己打电话去问结果;有没有被录取啊,还是被据了;
“Reactor”公司 HR 就记下了你的联系方式,结果出来后会主动打电话通知你:有没有被录取啊,还是被据了;你不用自己打电话去问结果,事实上也不能,你没有 HR 的留联系方式。
2 Reactor 模式的优点¶
Reactor 模式是编写高性能网络服务器的必备技术之一,它具有如下的优点
1)响应快,不必为单个同步时间所阻塞,虽然 Reactor 本身依然是同步的;
2)编程相对简单,可以最大程度的避免复杂的多线程及同步问题,并且避免了多线程/进程的切换开销; 3)可扩展性,可以方便的通过增加 Reactor 实例个数来充分利用 CPU 资源; 4)可复用性,reactor 框架本身与具体事件处理逻辑无关,具有很高的复用性;
3 Reactor 模式框架¶
使用 Reactor 模型,必备的几个组件:事件源、Reactor 框架、多路复用机制和事件处理程序,先来看看 Reactor 模型的整体框架,接下来再对每个组件做逐一说明。
1) 事件源 Linux 上是文件描述符,Windows 上就是 Socket 或者 Handle 了,这里统一称为“句柄集”;程序在指定的句柄上注册关心的事件,比如 I/O 事件。
2) event demultiplexer——事件多路分发机制 由操作系统提供的 I/O 多路复用机制,比如 select 和 epoll。 程序首先将其关心的句柄(事件源)及其事件注册到 event demultiplexer 上; 当有事件到达时,event demultiplexer 会发出通知“在已经注册的句柄集中,一个或多个句柄的事件已经就绪”; 程序收到通知后,就可以在非阻塞的情况下对事件进行处理了。 对应到 libevent 中,依然是 select、poll、epoll 等,但是 libevent 使用结构体 eventop 进行了封装,以统一的接口来支持这些 I/O 多路复用机制,达到了对外隐藏底层系统机制的目的。
3) Reactor——反应器 Reactor,是事件管理的接口,内部使用 event demultiplexer 注册、注销事件;并运行事件循环,当有事件进入“就绪”状态时,调用注册事件的回调函数处理事件。 对应到 libevent 中,就是 event_base 结构体。 一个典型的 Reactor 声明方式:
class Reactor{
public:
int register_handler(Event_Handler *pHandler, int event);
int remove_handler(Event_Handler *pHandler, int event);
void handle_events(timeval *ptv);
// ...
};
4) Event Handler——事件处理程序
事件处理程序提供了一组接口,每个接口对应了一种类型的事件,供 Reactor 在相应的事件发生时调用,执行相应的事件处理。通常它会绑定一个有效的句柄。 对应到 libevent 中,就是 event 结构体。 下面是两种典型的 Event Handler 类声明方式,二者互有优缺点。
class Event_Handler{
public:
virtual void handle_read() = 0;
virtual void handle_write() = 0;
virtual void handle_timeout() = 0;
virtual void handle_close() = 0;
virtual HANDLE get_handle() = 0;
// ...
};
class Event_Handler{
public:
// events maybe read/write/timeout/close .etc
virtual void handle_events(int events) = 0;
virtual HANDLE get_handle() = 0;
// ...
};
4¶
5 Reactor 事件处理流程¶
前面说过 Reactor 将事件流“逆置”了,那么使用 Reactor 模式后,事件控制流是什么样子呢? 可以参见下面的序列图。
6 小结¶
上面讲到了 Reactor 的基本概念、框架和处理流程,对 Reactor 有个基本清晰的了解后,再来对比看 libevent 就会更容易理解了,接下来就正式进入到 libevent 的代码世界了,加油!
参考资料: Pattern-Oriented Software Architecture, Patterns for Concurrent and Networked Objects, Volume 2