文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Redis事件驱动(aeEventLoop)原理分析

2024-11-30 13:15

关注
  1. 文件事件(File event)
  2. 时间事件(Time event)

文件事件是对相关的 fd 相关操作的封装,时间事件则是对定时任务相关操作的封装。Redis server通过文件事件来进行外部请求的处理与操作,通过时间事件来对系统内部产生的定时任务进行处理。(本文重点讲解文件事件相关的操作流程以及原理)

文中探讨的原理及源码基于Redis官方 v7.0 版本

Redis事件驱动的相关源码

在Redis源码中,涉及事件驱动相关的源码文件主要有以下几个(以ae作为文件名称前缀):

src
├── ae.c  
├── ae.h
├── ae_epoll.c
├── ae_evport.c
├── ae_kqueue.c
└── ae_select.c

根据源码中注释(ae.c)可知 ae 的含义为 A simple event-driven。

一个简单的事件驱动编程库。最初我(作者:antirez)为Jim的事件循环(Jim是Tcl解释器)编写了这段代码,但后来将其转化为库形式以便于重用。

多种i/o多路复用方法的选择

在Redis源码中存在多种i/o多路复用实现方式,如何选择使用哪种i/o多路复用实现呢?源码编译时选择不同的实现方式,即:Redis源码编译成二进制文件的时候来选择对应的实现方式,在源码可以看到蛛丝马迹。

代码文件: ae.c

#ifdef HAVE_EVPORT
#include "ae_evport.c"
#else
    #ifdef HAVE_EPOLL
    #include "ae_epoll.c"
    #else
        #ifdef HAVE_KQUEUE
        #include "ae_kqueue.c"
        #else
        #include "ae_select.c"
        #endif
    #endif
#endif

从上面代码可知,在编译源码的预处理阶段,根据不同的编译条件(#ifdef/#else/#endif)来判断对应的宏是否定义(#define定义的常量)来加载实现逻辑。以epoll为例,若定义了 HAVE_EPOLL 宏,则加载 "ae_epoll.c" 文件。宏 "HAVE_EVPORT/HAVE_EPOLL/HAVE_KQUEUE" 分别对应不同的系统(或者说是对应的编译器)。

代码文件: config.h

#ifdef __sun
#include 
#ifdef _DTRACE_VERSION
#define HAVE_EVPORT 1
#define HAVE_PSINFO 1
#endif
#endif

#ifdef __linux__
#define HAVE_EPOLL 1
#endif

#if (defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined (__NetBSD__)
#define HAVE_KQUEUE 1
#endif

假设,当前是linux系统,那么 宏__linux__ 又是从哪里来的呢?Linux环境下主要用gcc编译,借助 gcc -dM -E - < /dev/null 命令从获得相应的变量中可以看到其定义。

root@ivansli ~# gcc -dM -E - < /dev/null | grep __linux
#define __linux 1
#define __linux__ 1

即:Redis源码会根据编译器来判断应该把源码编译成对应平台(或者是通用平台,性能会有所下降)运行的二进制可执行程序。

核心结构体 aeEventLoop

aeEventLoop 结构体如下所示:


typedefstruct aeEventLoop {
    int maxfd;   
    int setsize; 
    longlong timeEventNextId; 

    
    
    
    
    aeFileEvent *events; 
    aeFiredEvent *fired; 

    aeTimeEvent *timeEventHead; 
    int stop; 
    void *apidata; 

    aeBeforeSleepProc *beforesleep;
    aeBeforeSleepProc *aftersleep;

    int flags;
} aeEventLoop;

aeEventLoop 结构体核心字段以及相关交互如下图所示:

// ae_epoll.c
typedefstruct aeApiState {
    int epfd; 
    struct epoll_event *events;
} aeApiState;

// ae_kqueue.c
typedefstruct aeApiState {
    int kqfd;
    struct kevent *events;

    
    char *eventsMask; 
} aeApiState;

// ae_evport.c
typedefstruct aeApiState {
    int     portfd;                             
    uint_t  npending;                           
    int     pending_fds[MAX_EVENT_BATCHSZ];     
    int     pending_masks[MAX_EVENT_BATCHSZ];   
} aeApiState;

// ae_select.c
typedefstruct aeApiState {
    fd_set rfds, wfds;
    
    fd_set _rfds, _wfds;
} aeApiState;

aeEventLoop 相关操作方法签名如下所示(文件ae.h):

aeEventLoop *aeCreateEventLoop(int setsize);
void aeDeleteEventLoop(aeEventLoop *eventLoop);
void aeStop(aeEventLoop *eventLoop);

int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask, aeFileProc *proc, void *clientData);
void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask);
int aeGetFileEvents(aeEventLoop *eventLoop, int fd);
void *aeGetFileClientData(aeEventLoop *eventLoop, int fd);

long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,
        aeTimeProc *proc, void *clientData, aeEventFinalizerProc *finalizerProc);
int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id);

int aeProcessEvents(aeEventLoop *eventLoop, int flags);
int aeWait(int fd, int mask, long long milliseconds);

void aeMain(aeEventLoop *eventLoop);

char *aeGetApiName(void);

void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep);
void aeSetAfterSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *aftersleep);

int aeGetSetSize(aeEventLoop *eventLoop);
int aeResizeSetSize(aeEventLoop *eventLoop, int setsize);
void aeSetDontWait(aeEventLoop *eventLoop, int noWait);

aeEventLoop事件处理核心方法

用途

调用i/o多路复用方法

epoll为例,调用方法

aeCreateEventLoop

创建并初始化事件循环

aeApiCreate

epoll_create()

默认水平触发

aeDeleteEventLoop

删除事件循环

aeApiFree

-

aeCreateFileEvent

创建文件事件

aeApiAddEvent

epoll_ctl()

EPOLL_CTL_ADD

EPOLL_CTL_MOD

aeDeleteFileEvent

删除文件事件

aeApiDelEvent

epoll_ctl()

EPOLL_CTL_MOD

EPOLL_CTL_DEL

aeProcessEvents

处理文件事件

aeApiPoll

epoll_wait()

aeGetApiName

获取i/o多路复用的实现名称

aeApiName

-

基于epoll的i/o多路复用

客户端与服务端的连接建立过程,如下图所示:

TCP三次握手时,Linux内核会维护两个队列:

  1. 半连接队列,被称为SYN队列
  2. 全连接队列,被称为 accept队列

epoll相关处理方法与逻辑如下图所示:

基于epoll的i/o多路复用伪代码框架:

int main(){
    lfd = socket(AF_INET,SOCK_STREAM,0); // 创建socket
    bind(lfd, ...); // 绑定IP地址与端口
    listen(lfd, ...); // 监听
 
    // 创建epoll对象
    efd = epoll_create(...);
    // 把 listen socket 的事件管理起来
    epoll_ctl(efd, EPOLL_CTL_ADD, lfd, ...);
 
    //事件循环
    for (;;) {
        size_t nready = epoll_wait(efd, ep, ...);
  
        for (int i = 0; i < nready; ++i){
            if(ep[i].data.fd == lfd){
                fd = accept(listenfd, ...); //lfd上发生事件表示都连接到达,accept接收它
                epoll_ctl(efd, EPOLL_CTL_ADD, fd, ...);
            }else{
                //其它socket发生的事件都是读写请求、或者关闭连接
                ...
            }
        }
    }
}

从上可知,Redis作为Server服务端在启动之后随时随刻监听着相关事件的发生。以linux为例,其处理过程与基于epoll的i/o多路复用伪代码框架基本相似,Redis源码中更多的是通过封装使其得到一个方便使用的库,库的底层包含了多种i/o多路复用实现方式。

aeEventLoop 的执行过程

以epoll为例,简化版的Redis事件驱动交互过程。

图中仅列出了核心方法,如有错误欢迎指正

Red括: 针对不同的 fd 注册 AE_READABLE/AE_WRITABLE 类型的回调方法,同时把 fd 添加到 epoll 中。当 fd 关心的事件触发之后,执行对应回调方法(主要针对 可读/可写/时间事件 3种类型的事件进行处理)。Redis 中 epoll 使用的触发方式为 LT 水平触发,意味着数据一次性没有处理完,下次 epoll_wait() 方法还会返回对应fd,直到处理完毕,对于客户端一次性发起批量处理多条命令的操作非常有益,减少对其他指令的阻塞时间。

来源:编程技术之道内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯