memcached 基于libevent的事件处理
1. libevent
在 memcached 中 libevent 被用来处理网络事件(连接请求,读和写) 或者被用来实现定时器。 使用 libevent 需要包含头文件 event.h, 并且在 GCC 链接时需要使用选项 -levent
相关函数的函数原型:
/* before using any of the functions in the library, * you must call event_init() or event_base_new() to * perform one-time initialization of the libevent library. */ struct event_base* event_init();
在所有其他库函数调用之前需要调用 event_init() 来初始化一个 event_base 结构。
// Associate a event with a callback (event handler) void event_set(struct event *event, evutil_socket_t fd, short what /* the event flags */, void(*callback)(evutil_socket_t, short, void *), void *arg); /* The event flags used in memcached * EV_TIMEOUT (0) This flag indicates an event that becomes active after a timeout elapses. The EV_TIMEOUT flag is ignored when constructing an event: you can either set a timeout when you add the event, or not. It is set in the 'what' argument to the callback function when a timeout has occurred. * EV_READ This flag indicates an event that becomes active when the provided file descriptor is ready for reading. * EV_WRITE This flag indicates an event that becomes active when the provided file descriptor is ready for writing. * EV_PERSIST Indicates that the event is persistent. See "About Event Persistence" below. */
调用 event_set() 来初始化一个 event 结构,并为其设置一个回调函数.
// Associate a different event base with an event. // The event to be associated must not be currently active or pending. // The return value is 0 on success, -1 on failure. int event_base_set(struct event_base *eb, struct event *ev)
调用 event_base_set() 将一个事件 与 一个 event_base 关联。
/** * Add an event to the set of pending events * @param ev : an event struct initialized via event_set() * @param timeout : the maximum amount of time to wait for the event, or NULL to wait forever. * @return : 0 on success, -1 on failure. */ int event_add(struct event *ev, struct timeval *timeout);
将事件添加到监听事件集中
/** * Delete a event. * @param ev : the event struct to be disabled * @return : 0 on success, -1 on failure. */ int event_del(struct event *ev);
将事件从监听事件集中移除
/* Loop to process events. In order to process events, * an application needs to call event_dispatch(). * event_base_dispatch() is threadsafe event dispatching loop. * event_base_loop() is a more flexible version of event_base_dispatch(). */ /** * @param eb : the event_base structure returned by event_base_new() or event_base_new_with_config() * @param flags : any combination of EVLOOP_ONCE | EVLOOP_NONBLOCK * @return : 0 on success, -1 on failure, or 1 if we exited because no events were pending or active */ int event_base_loop(struct event_base* eb, int flags); /* flags for event_base_loop : * 0 run the event base until either there are no more pending or active events, or until something calls event_base_loopbreak() or event_base_loopexit() * EVLOOP_ONCE(1) Block until we have an active event, then exit once all active events have had their callbacks run. * EVLOOP_NONBLOCK(2) Do not block: see which events are ready now, run the callbacks of the highest-priority ones, then exit. */
事件处理循环
// functions for libevent timers #define evtimer_set(ev, cb, arg) event_set((ev), -1, 0, (cb), (arg)) #define evtimer_add(ev, tv) event_add((ev), (tv)) #define evtimer_del(ev) event_del(ev)
References :
[1] http://www.wangafu.net/~nickm/libevent-2.0/doxygen/html/event_8h.html#_details
[2] http://www.wangafu.net/~nickm/libevent-book/
2. libevent timer
libevent can also be used to create timers that invoke a callback after a certain amount of time has expired.
The evtimer_set() function prepares an event struct to be used as a timer. To activate the timer, call evtimer_add().
Timers can be deactivated by calling evtimer_del(). (From http://www.monkey.org/~provos/libevent/doxygen-2.0.1/)
大意是: libevent也能被用来创建定时器,当时间到期时,能够调用一个回调函数。调用 evtimer_set() 函数初始化 timer,调用 evtimer_add() 函数激活 timer,
调用 evtimer_del() 函数删除 timer。这三个函数的定义见第1小节。
下面源代码是从 memcached.c 中抽取出来的利用 libevent timer 来更新进程运行时间的代码。
#include <event.h> // libevent header #include <sys/time.h> // for gettimeofday #include <stdbool.h> // for bool #include <stdlib.h> // for time(), EXIT_SUCCESS and EXIT_FAILURE /** the code below is from memcached source **/ typedef unsigned int rel_time_t; /** in memcached.h **/ // ... time_t process_started; /* when the process was started */ // ... static struct event_base * main_base; // ... /* * We keep the current time of day in a global variable that's updated by a * timer event. This saves us a bunch of time() system calls (we really only * need to get the time once a second, whereas there can be tens of thousands * of requests a second) and allows us to use server-start-relative timestamps * rather than absolute UNIX timestamps, a space savings on systems where * sizeof(time_t) > sizeof(unsigned int). */ volatile rel_time_t current_time; static struct event clockevent; static void clock_handler(const int fd, const short which, void *arg) { struct timeval t = {.tv_sec = 1, .tv_usec = 0}; static bool initialized = false; if (initialized) { /* only delete the event if it's actually there. */ evtimer_del(&clockevent); } else { initialized = true; } evtimer_set(&clockevent, clock_handler, 0); event_base_set(main_base, &clockevent); evtimer_add(&clockevent, &t); struct timeval tv; gettimeofday(&tv, NULL); current_time = (rel_time_t) (tv.tv_sec - process_started); } // ... int main(int argc, char* argv[]) { // ... int retval = EXIT_SUCCESS; // ... main_base = event_init(); #define ITEM_UPDATE_INTERVAL 60 /** originally in memcached.h **/ process_started = time(0) - ITEM_UPDATE_INTERVAL - 2; /** originally in stats_init() **/ // ... clock_handler(0, 0, 0); // ... /* enter the event loop */ if (event_base_loop(main_base, 0) != 0) { retval = EXIT_FAILURE; } // ... return retval; }
memcached 实现中还利用 libevent timer 处理网络连接,具体在memcached网络通信(TODO: add a link here)中会有描述。