Reactor模式是编写高性能网络服务器的必备技术之一,它具有如下的优点:
- 响应快,不必为单个同步时间所阻塞,虽然 Reactor 本身依然是同步的;
- 编程相对简单,可以最大程度的避免复杂的多线程及同步问题,并且避免了多线程/
- 进程的切换开销;
- 可扩展性,可以方便的通过增加 Reactor 实例个数来充分利用 CPU 资源;
- 可复用性, reactor 框架本身与具体事件处理逻辑无关,具有很高的复用性
下图描述了Reactor模式的框架,主要包括:事件源、框架部分(Reactor)、事件多路分发机制(event demultiplexing)、事件处理程序(event handler)。
1、事件源:Linux 上是文件描述符, Windows 上就是 Socket 或者 Handle 了,这里统一称为“句柄集”;程序在指定的句柄上注册关心的事件,比如 I/O 事件。
在libevent中有三种类型的事件:定时器事件(time event)、信号事件(signal event)和I/O事件。
2、事件多路分发机制(event demultiplexing)
需要使用底层提供的多路复用机制,如evport, select , poll, epoll, kqueue, devpoll. 用户进程首先在event demultiplexing上注册事件,采用合适的多路复用机制检测事件,当事件发生时,event demultiplexing发出通知“在已经注册的事件集中,一个或多个事件已经就绪“,程序收到通知后对事件进行处理。
1)libevent中对多路复用机制进行了封装,使得根据操作系统,可以选择最高效的IO机制。
static const struct eventop *eventops[] = {
#ifdef _EVENT_HAVE_EVENT_PORTS
&evportops,
#endif
#ifdef _EVENT_HAVE_WORKING_KQUEUE
&kqops,
#endif
#ifdef _EVENT_HAVE_EPOLL
&epollops,
#endif
#ifdef _EVENT_HAVE_DEVPOLL
&devpollops,
#endif
#ifdef _EVENT_HAVE_POLL
&pollops,
#endif
#ifdef _EVENT_HAVE_SELECT
&selectops,
#endif
#ifdef WIN32
&win32ops,
#endif
NULL
};
2)事件注册:
首先,对event进行初始化,并将event与event_base(可以理解为事件库)关联起来,如下:
event_new(struct event_base *base, evutil_socket_t fd, short events, void (*cb)(evutil_socket_t, short, void *), void *arg)
其中cb表示事件处理函数,也即回调函数,需要用户实现。
然后,将事件添加到事件库,此时event的状态为pending:
int event_add(struct event *ev, const struct timeval *tv);
3)事件触发:
在事件加入event_base后,选择合适的多路复用机制遍历事件队列,将状态为激活(active)的事件插入到激活队列中,从高到低优先级遍历激活event优先级数组。对于激活的event,调用event_queue_remove将之从激活队列中删除掉。然后再对这个event调用其回调函数。
整个Reactor的流程如下: