一、简介
因memcached(见上一篇“数据缓存系统-memcached介绍”)使用了libevent作为它对外界事件(磁盘、网络等)的监控,所以对libevent就一同进行了了解、应用。
按libevent的官网(http://libevent.org/)介绍,libevent是一个监视特定事件,提供回调函数的C++库,它可以监视的事件包括:
1. 能以文件描述符表示的事件(网络、文件等)
2. Signal信号(似乎对Linux平台适用)
3. 定时事件(timeout)
目前,libevent支持的平台事件监视函数,包括/dev/poll, kqueue(2), event ports, POSIX select(2), Windowsselect(), poll(2), epoll(4),它可以用于多线程应用,由于隔离了底层调用,上层与底层的更新互不影响,除了对事件的监视,libevent还提供了以下的辅助功能:
1. 带buffer的网络IO-这个东西是libevent中读取网络的接口。
2. 简单的DNS,HTTP Sever和RPC框架
总之,libevent作为网络编程框架,其功能还是比较强大的,官网中有一个采用libevent的应用列表,其中就包括memcached。
(网络编程采用事件回调的方式,大概是高负荷网络IO应用的主要模式,两个主要的开源Web服务器nginx,lighttpd无一例外的采用事件驱动方式。)
二、版本
在1.X版,初始化event用的是event_init,它默认的是全局一个event base,这被认为不是线程安全的,所以,2.X以后,结构event base被暴露出来供用户使用,一些1.X的API不再适用了。
目前libevent的版本是2.0.20,也是本文采用的版本,网上一半以上的文章介绍的都是老版本,是不能在新版本下编译的。不过,libevent还是提供了类似XXX-compat.h这样的include文件,供老版本使用。所以,如果采用老版本,应包含XXX-compat.h这样的头文件。
Libevent2.0版本中,有一个目录event2,专门指的是2.0版本的。
三、编译
在linux下,该操作是一个典型编译操作,命令如下:
$ ./configure在windows下,可以用以下命令,前提是需要安装vc(vs)。
$ make
$ make verify # (optional)
$ sudo make install
nmake –f makefile.nmake
编译完毕后,应该有以下三个库:
- libevent_core:所有核心事件功能和buffer功能,包括event_base,evbuffer, bufferevent, utility。
- libevent_extra:提供某些特定协议的功能,包括HTTP, DNS, RPC。
- Libevent:因历史原因而保留的库,包含以上两个库的内容, 不应再使用,以后可能消失。
因此,大部分情况下,我们需要的是libevent_core库。
四、组成和功能
libevent包含如下部分:
- evutil
它抽象了一组辅助功能,并屏蔽了操作系统平台的不同。
- event 及 event_base
这是libevent的核心,提供了一组不依赖于操作系统平台的基于事件的非阻塞IO操作。
- bufferevent
这个功能基于libevent的事件驱动,提供了非常方便的IO读写,它也可以有多个后端支持,以充分利用操作系统的功能,例如Windows的IOCP API。
- evbuffer
这个模块是bufferevent的基础,提供了高效的读写方法。
- evhttp
一个简单的HTTP client/server实现。
- evdns
一个简单的DNS client/server实现。
- evrpc
一个简单的RPC实现。
五、基本应用框架
一个典型的libevent的应用大致需要如下步骤:
(环境设置)-> (创建event_base) -> (创建event,将此event加入到event_base中) -> (设置event各种属性,事件等) ->(将event加入事件列表 addevent) ->(开始事件监视循环、分发dispatch)。
监视事件发生后,事件处理函数可以只被调用一次或总被调用。
- 只调用一次:事件处理函数被调用后,即从事件队列中删除,需要在事件处理函数中再次加入事件,才能在下次事件发生时被调用;
- 总被调用:设置为EV_PERSIST,只加入一次,处理函数总被调用,除非采用event_remove显式地删除。
如果想退出事件监视循环,可调用event_base_loopexit,此时,event_base_dispatch会退出。
libevent中提供了需要辅助函数,例如listener.h中对listen,bind等的封装,http.h中对许多http应用的封装等,可以使用这些函数,读写网络时,似乎只能使用bufferevent中的东西。
目前的设计中,如果是多线程,建议一个线程一个event_base,互不影响,如果一个event_base需要多个线程共享,则需要加锁。
六、其他考虑
作为研究,到上一节的介绍就够啦,但正式应用libevent到你的商业系统,还需要考虑如下问题,
- 日志
Libevent可以记录错误信息以及调试信息,缺省将其输出到stderr,但嵌入到应用系统后,一般采用应用系统的日志系统,此时,可以修改libevent的日志记录方式,调用event_set_log_callback,需要注意的是自己的日志记录函数不要使用libevent的函数,否则有可能发生难以预料的结果(循环调用)。
- 错误退出
如果libevent发生不可恢复的错误(无论是你的事件代码还是它的代码),将调用exit或abort退出进程,调用event_set_fatal_callback可以修改这种行为,同样自定义函数不要使用libevent的函数。
- 内存管理
Libevent采用C库中的内存管理函数分配和释放内存,调用event_set_mem_functions可以修改这种方式。(一般我们无需修改)
- 多线程
如果采用windows平台或linux平台,系统提供了缺省的多线程支持,否则需要自己实现诸如创建线程、加锁、解锁等相关函数,然后通过evthread_set_lock_callbacks、evthread_set_id_callback告诉libevent。(看样子想完全跨平台还需自己努力了)
- 调试
Libevent可以设置为调试状态,调用event_enable_debug_mode,当然仅在开发时使用。
(待续,本文只给出libevent的文字介绍,下一篇给出具体的实例。)