• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

深入剖析 redis 事件驱动

mysql 搞代码 4年前 (2022-01-09) 14次浏览 已收录 0个评论

概述 redis 内部有一个小型的事件驱动,它和 libevent 网络库的事件驱动一样,都是依托 I/O 多路复用技术支撑起来的。 利用 I/O 多路复用技术,监听感兴趣的文件 I/O 事件,例如读事件,写事件等,同时也要维护一个以文件描述符为主键,数据为某个预设函数的

概述

redis 内部有一个小型的事件驱动,它和 libevent 网络库的事件驱动一样,都是依托 I/O 多路复用技术支撑起来的。

利用 I/O 多路复用技术,监听感兴趣的文件 I/O 事件,例如读事件,写事件等,同时也要维护一个以文件描述符为主键,数据为某个预设函数的事件表,这里其实就是一个数组或者链表 。当事件触发时,比如某个文件描述符可读,系统会返回文件描述符值,用这个值在事件表中找到相应的数据项,从而实现回调。同样的,定时事件也是可以实现的,因为系统提供的 I/O 多路复用技术中的函数允许我们设定时间值。

上面一段话比

本文来源gao!%daima.com搞$代*!码$网3

较综合,可能需要一些 linux 系统编程和网络编程的基础,但你会看到多数事件驱动程序都是这么实现的(?)。

redis 事件驱动数据结构

redis 事件驱动内部有四个主要的数据结构,分别是:事件循环结构体,文件事件结构体,时间事件结构体和触发事件结构体。

// 文件事件结构体/* File event structure */typedef struct aeFileEvent {    int mask; /* one of AE_(READABLE|WRITABLE) */    // 回调函数指针    aeFileProc *rfileProc;    aeFileProc *wfileProc;    // clientData 参数一般是指向 redisClient 的指针    void *clientData;} aeFileEvent;// 时间事件结构体/* Time event structure */typedef struct aeTimeEvent {    long long id; /* time event identifier. */    long when_sec; /* seconds */    long when_ms; /* milliseconds */    // 定时回调函数指针    aeTimeProc *timeProc;    // 定时事件清理函数,当删除定时事件的时候会被调用    aeEventFinalizerProc *finalizerProc;    // clientData 参数一般是指向 redisClient 的指针    void *clientData;    // 定时事件表采用链表来维护    struct aeTimeEvent *next;} aeTimeEvent;// 触发事件/* A fired event */typedef struct aeFiredEvent {    int fd;    int mask;} aeFiredEvent;// 事件循环结构体/* State of an event based program */typedef struct aeEventLoop {    int maxfd;   /* highest file descriptor currently registered */    int setsize; /* max number of file descriptors tracked */    // 记录最大的定时事件 id + 1    long long timeEventNextId;    // 用于系统时间的矫正    time_t lastTime;     /* Used to detect system clock skew */    // I/O 事件表    aeFileEvent *events; /* Registered events */    // 被触发的事件    aeFiredEvent *fired; /* Fired events */    // 定时事件表    aeTimeEvent *timeEventHead;    // 事件循环结束标识    int stop;    // 对于不同的 I/O 多路复用技术,有不同的数据,详见各自实现    void *apidata; /* This is used for polling API specific data */    // 新的循环前需要执行的操作    aeBeforeSleepProc *beforesleep;} aeEventLoop;

上面的数据结构能给我们很好的提示:事件循环结构体维护 I/O 事件表,定时事件表和触发事件表。

事件循环中心

redis 的主函数中调用 initServer() 函数从而初始化事件循环中心(EventLoop),它的主要工作是在 aeCreateEventLoop() 中完成的。

aeEventLoop *aeCreateEventLoop(int setsize) {    aeEventLoop *eventLoop;    int i;    // 分配空间    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;    // 进入事件循环前需要执行的操作,此项会在 redis main() 函数中设置    eventLoop->beforesleep = NULL;    // 在这里,aeApiCreate() 函数对于每个 IO 多路复用模型的实现都有不同,具体参见源代码,因为每种 IO 多路复用模型的初始化都不同    if (aeApiCreate(eventLoop) == -1) goto err;    /* Events with mask == AE_NONE are not set. So let's initialize the     * vector with it. */    // 初始化事件类型掩码为无事件状态    for (i = 0; i events[i].mask = AE_NONE;    return eventLoop;err:    if (eventLoop) {        zfree(eventLoop->events);        zfree(eventLoop->fired);        zfree(eventLoop);    }    return NULL;}

搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:深入剖析 redis 事件驱动
喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址