redis ae事件驱动的源码分析

本文深入分析redis的事件驱动模型,重点探讨文件事件和时间事件的处理。文件事件由reactor模式的文件事件处理器处理,包括套接字、I/O多路复用和调度器。多路复用程序如select、epoll等通过aeApi接口统一。时间事件主要为定时和周期性执行的serverCron,用于服务器维护。aeProcessEvents在事件调度中起到关键作用。
摘要由CSDN通过智能技术生成

       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;
}//撤销时间事件的监听,从时间事件链表中删除aeTimeEvent
int 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);
    }
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值