基于事件的开源网络库—libevent:应用介绍

时间:2021-02-19 00:14:58

一、简介

因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
$ make
$ make verify # (optional)
$ sudo make install
在windows下,可以用以下命令,前提是需要安装vc(vs)。

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的文字介绍,下一篇给出具体的实例。)