redis ae事件驱动的源码分析

来源:互联网 发布:顶级域名cc 编辑:程序博客网 时间:2024/06/05 09:12

       redis是一个事件驱动的服务器,主要处理两类事件:文件事件和时间事件。事件驱动模型的主要数据结构

如下:

/* 文件事件数据结构*/typedef struct aeFileEvent {    int mask; /* 文件事件的类型:READABLE|WRITABLE */    aeFileProc *rfileProc;//读事件的处理器    aeFileProc *wfileProc;//写事件的处理器    void *clientData;//client相关的数据} aeFileEvent;/* 时间时间数据结构 */typedef struct aeTimeEvent {    long long id; /* 时间事件id */    long when_sec; //时间事件到达的时间,精度为秒    long when_ms; //时间事件到达的时间,精度为毫秒    aeTimeProc *timeProc;//时间事件处理器    aeEventFinalizerProc *finalizerProc;//处理完时间事件的收尾方法    void *clientData;    struct aeTimeEvent *next;//下一个注册的时间事件} aeTimeEvent;/* 触发事件的数据结构 */typedef struct aeFiredEvent {    int fd;    int mask;} aeFiredEvent;/* 事件驱动模型的数据结构*/typedef struct aeEventLoop {    int maxfd;   /* 当前注册的文件事件的最大文件描述符*/    int setsize; /* 注册文件事件的文件描述符的最大限制 */    long long timeEventNextId;//下一次注册的时间事件的id    time_t lastTime;     /* Used to detect system clock skew */    aeFileEvent *events; /* 注册的文件事件 */    aeFiredEvent *fired; /* 触发的事件*/    aeTimeEvent *timeEventHead;//注册的时间事件    int stop;//用于暂停事件驱动模型    void *apidata;//用于底层封装的多路io    aeBeforeSleepProc *beforesleep;//进入事件驱动等待事件触发前调用的函数} aeEventLoop;
一、文件事件

         服务器与客户端的通信产生的文件事件,而服务器基于reactor模式开发文件事件处理器来完成一系列

文件事件的处理。 文件事件处理器主要套接字、I/O多路复用程序、文件事件调度器和文件事件处理器。文件

事件是对套接字操作的抽象,套接字准备好执行连接应答、写入、读取,关闭等操作都会产生一个文件事件。

       redis的多路复用程序的功能都是通过包装了常见的select、epoll、evport和kqueue这些多路复用函数库来

实现。为了接口统一,redis简单封装了这些函数到对应的文件,比如ae_select.c、ae_epoll.c和kqueue.c等文件。

       文件事件的注册和撤销主要基于多路复用函数库的aeApiAddEvent和aeApiDelEvent实现。

//对fd进行给定的事件的监听int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,        aeFileProc *proc, void *clientData){//fd大于事件驱动模型设置的上限    if (fd >= eventLoop->setsize) {        errno = ERANGE;        return AE_ERR;    }    aeFileEvent *fe = &eventLoop->events[fd];    //aeApiAddEvent为简单封装过的多路复用中的事件注册函数    if (aeApiAddEvent(eventLoop, fd, mask) == -1)        return AE_ERR;    fe->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;}// 取消对fd进行的给定事件的监听void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask){    if (fd >= eventLoop->setsize) return;    aeFileEvent *fe = &eventLoop->events[fd];    if (fe->mask == AE_NONE) return;    //aeApiDelEvent为简单封装过的多路复用中的事件删除注册函数    aeApiDelEvent(eventLoop, fd, mask);    fe->mask = fe->mask & (~mask);    if (fd == eventLoop->maxfd && fe->mask == AE_NONE) {        int j;        for (j = eventLoop->maxfd-1; j >= 0; j--)            if (eventLoop->events[j].mask != AE_NONE) break;        eventLoop->maxfd = j;//更新事件模型的最大注册文件描述符    }}

      文件事件的调度和执行主要是在事件驱动模型中的aeProcessEvents的实现。

二、时间事件

       redis中的时间事件主要分为定时事件和周期性事件。redis时间事件只有周期性执行serverCron,定期对

对自身的资源和状态进行检查和调整,从而确保服务器正常运行。

     时间事件的注册和取消主要是通过一个保存时间事件的链表来实现。

//注册时间事件进行监听,将aeTimeEvent加入到时间事件链表中long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,        aeTimeProc *proc, void *clientData,        aeEventFinalizerProc *finalizerProc){    long long id = eventLoop->timeEventNextId++;    aeTimeEvent *te;    te = zmalloc(sizeof(*te));    if (te == NULL) return AE_ERR;    //初始化时间时间的属性    te->id = id;    aeAddMillisecondsToNow(milliseconds,&te->when_sec,&te->when_ms);    te->timeProc = proc;    te->finalizerProc = finalizerProc;    te->clientData = clientData;    //添加时间事件到链表    te->next = eventLoop->timeEventHead;    eventLoop->timeEventHead = te;    return id;}//撤销时间事件的监听,从时间事件链表中删除aeTimeEventint aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id){    aeTimeEvent *te = eventLoop->timeEventHead;    while(te) {        if (te->id == id) {            te->id = AE_DELETED_EVENT_ID;            return AE_OK;        }        te = te->next;    }    return AE_ERR; /* NO event with the specified ID found */}
      时间事件的调度和执行主要是在事件驱动模型中的processTimeEvents的实现,processTimeEvents在

aeProcessEvents被调用。

static int processTimeEvents(aeEventLoop *eventLoop) {    int processed = 0;    aeTimeEvent *te, *prev;    long long maxId;    time_t now = time(NULL);    if (now < eventLoop->lastTime) {        te = eventLoop->timeEventHead;        while(te) {            te->when_sec = 0;            te = te->next;        }    }    eventLoop->lastTime = now;    prev = NULL;    te = eventLoop->timeEventHead;    maxId = eventLoop->timeEventNextId-1;    //遍历时间事件链表,获取触发的时间事件    while(te) {        long now_sec, now_ms;        long long id;        /* 删除被调度的时间事件 */        if (te->id == AE_DELETED_EVENT_ID) {            aeTimeEvent *next = te->next;            if (prev == NULL)                eventLoop->timeEventHead = te->next;            else                prev->next = te->next;            if (te->finalizerProc)                te->finalizerProc(eventLoop, te->clientData);            zfree(te);            te = next;            continue;        }        if (te->id > maxId) {            te = te->next;            continue;        }        aeGetTime(&now_sec, &now_ms);        if (now_sec > te->when_sec ||            (now_sec == te->when_sec && now_ms >= te->when_ms))        {//执行触发的时间事件的处理器            int retval;            id = te->id;            retval = te->timeProc(eventLoop, id, te->clientData);            processed++;            if (retval != AE_NOMORE) {                aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms);            } else {//标记时间事件已经被调度执行过了                te->id = AE_DELETED_EVENT_ID;            }        }        prev = te;        te = te->next;    }    return processed;}
三、事件驱动模型

1、事件驱动模型的初始化

aeEventLoop *aeCreateEventLoop(int setsize) {    aeEventLoop *eventLoop;    int i;//创建aeEventLoop结构体,并初始化属性    if ((eventLoop = zmalloc(sizeof(*eventLoop))) == NULL) goto err;    eventLoop->events = zmalloc(sizeof(aeFileEvent)*setsize);    eventLoop->fired = zmalloc(sizeof(aeFiredEvent)*setsize);    if (eventLoop->events == NULL || eventLoop->fired == NULL) goto err;    eventLoop->setsize = setsize;    eventLoop->lastTime = time(NULL);    eventLoop->timeEventHead = NULL;    eventLoop->timeEventNextId = 0;    eventLoop->stop = 0;    eventLoop->maxfd = -1;    eventLoop->beforesleep = NULL;    if (aeApiCreate(eventLoop) == -1) goto err;    for (i = 0; i < setsize; i++)        eventLoop->events[i].mask = AE_NONE;    return eventLoop;err:    if (eventLoop) {        zfree(eventLoop->events);        zfree(eventLoop->fired);        zfree(eventLoop);    }    return NULL;}
2、事件的调度和执行
int aeProcessEvents(aeEventLoop *eventLoop, int flags){//processed记录这次调度执行了多少事件    int processed = 0, numevents;    if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0;    if (eventLoop->maxfd != -1 ||        ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {        int j;        aeTimeEvent *shortest = NULL;        struct timeval tv, *tvp;        if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))            //获取最近将要发生的时间事件            shortest = aeSearchNearestTimer(eventLoop);        //计算aeApiPoll的超时时间        if (shortest) {            long now_sec, now_ms;            //获取当前时间            aeGetTime(&now_sec, &now_ms);            tvp = &tv;            //计算距离下一次发生时间时间的时间间隔            long long ms =                (shortest->when_sec - now_sec)*1000 +                shortest->when_ms - now_ms;            if (ms > 0) {                tvp->tv_sec = ms/1000;                tvp->tv_usec = (ms % 1000)*1000;            } else {                tvp->tv_sec = 0;                tvp->tv_usec = 0;            }        } else {//没有时间事件            if (flags & AE_DONT_WAIT) {//马上返回,不阻塞                tv.tv_sec = tv.tv_usec = 0;                tvp = &tv;            } else {                tvp = NULL; //阻塞到文件事件发生            }        }//等待文件事件发生,tvp为超时时间,超时马上返回(tvp为0表示马上,为null表示阻塞到事件发生)        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;            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++;        }    }    if (flags & AE_TIME_EVENTS)//时间事件调度和执行        processed += processTimeEvents(eventLoop);    return processed;}

3、事件驱动模型的启动函数

void aeMain(aeEventLoop *eventLoop) {    eventLoop->stop = 0;    //循环的调用事件驱动调度和执行    while (!eventLoop->stop) {        if (eventLoop->beforesleep != NULL)        //进入事件驱动等待事件触发前的函数调用            eventLoop->beforesleep(eventLoop);        //事件驱动调度和执行        aeProcessEvents(eventLoop, AE_ALL_EVENTS);    }}







0 0
原创粉丝点击