redis的事件驱动

学习和整理了前面两篇的知识,现在开始进入这次的重点redis的ae源码了。
redis自己封装的ae事件驱动的主体结构

/* State of an event based program */
typedef struct aeEventLoop {
    int maxfd;   /* highest file descriptor currently registered */
    int setsize; /* max number of file descriptors tracked */
    long long timeEventNextId;
    time_t lastTime;     /* Used to detect system clock skew */
    aeFileEvent *events; /* Registered events */
    aeFiredEvent *fired; /* Fired events */
    aeTimeEvent *timeEventHead;
    int stop;
    void *apidata; /* This is used for polling API specific data */
    aeBeforeSleepProc *beforesleep;
} aeEventLoop;

这里我们重点关注,aeFileEvent *events 这个文件事件

/* File event structure */
typedef struct aeFileEvent {
    int mask;              //用来记录注册的事件(struct epoll.events)
    aeFileProc *rfileProc; //读操作回调
    aeFileProc *wfileProc; //写操作回调
    void *clientData;      //client的数据
} aeFileEvent;

接下来注册一个文件事件

int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
        aeFileProc *proc, void *clientData)
{
    if (fd >= eventLoop->setsize) {
        errno = ERANGE;
        return AE_ERR;
    }
    //用文件描述符作为下标存储在eventLoop->events中
    aeFileEvent *fe = &eventLoop->events[fd];

    //这里的aeApiAddEvent 就是用epoll_ctl注册这个文件描述的事件监听
    if (aeApiAddEvent(eventLoop, fd, mask) == -1)
        return AE_ERR;
    fe->mask |= mask;
    //根据mask设置回调函数是读还是写
    if (mask & AE_READABLE) fe->rfileProc = proc;
    if (mask & AE_WRITABLE) fe->wfileProc = proc;
    fe->clientData = clientData;
    if (fd > eventLoop->maxfd)
        eventLoop->maxfd = fd;
    return AE_OK;
}

这样就注册了一个文件的事件了。接下来redis用一个aeMain做一个循环一直监听和处理事件

int aeProcessEvents(aeEventLoop *eventLoop, int flags)
{
    /**
    .
    .
    省略部分代码
    .
    .
    **/

    //aeApiPoll 调用epoll_wait回去事件
    int processed = 0, numevents;
    numevents = aeApiPoll(eventLoop, tvp);

    for (j = 0; j < numevents; j++) {
        aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];
        int mask = eventLoop->fired[j].mask;
        int fd = eventLoop->fired[j].fd;
        int rfired = 0;

        //根据mask判断执行哪个回调
        if (fe->mask & mask & AE_READABLE) {
            rfired = 1;
            fe->rfileProc(eventLoop,fd,fe->clientData,mask);
        }   
        if (fe->mask & mask & AE_WRITABLE) {
            if (!rfired || fe->wfileProc != fe->rfileProc)
                fe->wfileProc(eventLoop,fd,fe->clientData,mask);
        }   
        processed++;
    }   
}

void aeMain(aeEventLoop *eventLoop) {
    eventLoop->stop = 0;
    while (!eventLoop->stop) {
        if (eventLoop->beforesleep != NULL)
            eventLoop->beforesleep(eventLoop);
        aeProcessEvents(eventLoop, AE_ALL_EVENTS);
    }
}

这样一个事件处理的创建到处理的方法大致的都在这里了。一些细节还是要继续看源代码。
redis除了文件事件还有时间时间,redis用单链表存储,每次遍历查看到时间的事件然后触发,看到了很多博客都说可以用最小堆之类的数据结构优化,其实是没有必要的,可以全局搜索aeCreateTimeEvent(注册时间事件的方法),只在redis.c的initServer 初始化redis serverCron 和 redis-benchmark.c中,总共有两次,也就是说redis server的timeEvent 也就是个位数的数量级,没看错的就是1个,所以根本没有必要做优化。

你可能感兴趣的:(redis)