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)中会有描述。

原文地址:https://www.cnblogs.com/william-cheung/p/4801549.html