Redis事件驱动(aeEventLoop)原理分析
编程技术之道 2023-05-31 13:05:57
关于Redis事件驱动

众所周知,Redis是高性能的、基于内存的、k-v数据库。其强大的功能背后,存在着2种不同类型的事件驱动,包括:

文件事件(File event)时间事件(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.h文件事件驱动/时间事件驱动结构体、方法签名的定义ae_epoll.c linux os 文件事件驱动涉及的i/o多路复用实现ae_evport.c sun os 文件事件驱动涉及的i/o多路复用实现ae_kqueue.c mac/BSD os 文件事件驱动涉及的os i/o多路复用实现ae_select.c 其他 os 文件事件驱动涉及的i/o多路复用实现(或者说是通用型的,包括Windows)

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

/* A simple event-driven programming library. Originally I wrote this code * for the Jim"s event-loop (Jim is a Tcl interpreter) but later translated * it in form of a library for easy reuse. */

一个简单的事件驱动编程库。最初我(作者: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 结构体如下所示:

/* State of an event based program 事件驱动程序的状态 */typedefstruct aeEventLoop {    int maxfd;   /* highest file descriptor currently registered. 当前已注册的最高文件描述符 */    int setsize; /* max number of file descriptors tracked. [events/fired数组的大小] */    longlong timeEventNextId; /* 时间事件的下一个ID */    /* events/fired 都是数组 */    /* events 数组,下标含义:为某个fd。fd=>aeFileEvent,即 文件描述符=>文件事件 */    /* fired 为 io多路复用返回的数组,每一个值为就绪的fd */    /* 通过 fired 中的 fd 去 events 查找对应的事件信息(事件信息包含conn) */    aeFileEvent *events; /* Registered events 已注册事件,数组 */    aeFiredEvent *fired; /* Fired events 触发的事件,数组 */    aeTimeEvent *timeEventHead; /* 时间事件,链表 */    int stop; /* 停止事件循环 */    void *apidata; /* This is used for polling API specific data. 这用于获取特定的API数据,aeApiState *state 包含io多路复用fd等字段 */    aeBeforeSleepProc *beforesleep;    aeBeforeSleepProc *aftersleep;    int flags;} aeEventLoop;

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

setsize 文件事件数组大小,等于 server.maxclients+CONFIG_FDSET_INCRevents 文件事件数组,大小等于setsizefired 文件事件就绪的fd数组,大小等于setsizetimeEventHead 时间事件数组,双向链表apidata 这用于获取特定的API数据,指向 aeApiState结构体,不同的i/o多路复用实现包含不同的字段。
// ae_epoll.ctypedefstruct aeApiState {/* 在 aeApiCreate 中初始化,linux则在 ae_linux.c 文件 */    int epfd; /* io多路复用fd */    struct epoll_event *events;/* 就绪的事件数组  */} aeApiState;// ae_kqueue.ctypedefstruct aeApiState {    int kqfd;    struct kevent *events;    /* Events mask for merge read and write event.     * To reduce memory consumption, we use 2 bits to store the mask     * of an event, so that 1 byte will store the mask of 4 events. */    char *eventsMask; } aeApiState;// ae_evport.ctypedefstruct aeApiState {    int     portfd;                             /* event port */    uint_t  npending;                           /* # of pending fds */    int     pending_fds[MAX_EVENT_BATCHSZ];     /* pending fds */    int     pending_masks[MAX_EVENT_BATCHSZ];   /* pending fds" masks */} aeApiState;// ae_select.ctypedefstruct aeApiState {    fd_set rfds, wfds;    /* We need to have a copy of the fd sets as it"s not safe to reuse     * FD sets after select(). */    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内核会维护两个队列:

半连接队列,被称为SYN队列全连接队列,被称为 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,直到处理完毕,对于客户端一次性发起批量处理多条命令的操作非常有益,减少对其他指令的阻塞时间。

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

2023-05-31 13:05:57

绿色消费应有更宽广视野 当前关注

2023-05-31 12:17:56

2023-2029年中国新能源汽车行业分析与发展趋势研究报告

2023-05-31 11:57:12

快看点丨国家金融监督管理总局办公厅副主任张忠宁:将进一步完善养老服务业金融支持政策

2023-05-31 11:34:02

抖音直播打击网络黑灰产 清理违规账号23577个

2023-05-31 11:18:54

冲凉是什么意思_冲凉 焦点热门

2023-05-31 10:55:23

郑州人才驿站实现全市覆盖_世界聚焦

2023-05-31 10:11:45

永城市督查局 市久和公益组织“六一”慰问新桥镇中心小学

2023-05-31 10:04:56

环球最资讯丨新股申购(打新小闹钟)2023年05月31日

2023-05-31 09:57:04

全球热消息:北京启动职业学校办学条件达标工程,师生比、生均图书等纳入考核

2023-05-31 09:25:10

中国轻纺城:5月31日混纺布行情|热讯

2023-05-31 08:48:53

每日头条!支付宝网站登录账户(网易支付宝登陆)

2023-05-31 08:06:39

世界资讯:神舟十六号探宇 太空之家再迎“新成员”

2023-05-31 08:09:29

“邵阳红·周末志愿行”新时代文明实践志愿服务走进立新社区|全球观速讯

2023-05-31 07:58:19

泰恩康(301263):5月30日北向资金减持7.12万股_新动态

2023-05-31 07:05:15

立升净水器售后服务电话(立升净水器好吗)_速讯

2023-05-31 06:55:06

环球视讯!骑士小说完本(骑士小说阅读网)

2023-05-31 06:18:47

聚焦:新华全媒+|绘出航天强国的样子——写在神舟十六号载人飞船成功发射之际

2023-05-31 05:50:58

板庙村_关于板庙村概略_焦点短讯

2023-05-31 05:28:08

环球视讯!03g363是否作废_03g363

2023-05-31 04:47:41

天天观察:“致富果”大丰收

2023-05-31 03:53:36

国内核电核准项目上升态势明显,千亿市场有望得到释放-环球快看点

2023-05-31 02:47:02

追踪金选|一天杀一只创新药,贝达药业被捶,跌了近20%,公司紧急送上新买管线以稳市场信心? 每日播报

2023-05-31 02:01:01

天天热消息:618智能手表怎么选最值?对比华为、苹果、OPPO:懂了

2023-05-31 00:36:18

股东大会五粮液管理团队坦诚相对,给投资者吃下“定心丸”-环球观天下

2023-05-30 23:54:25

环球热点!【原】投影仪设置使用步骤

2023-05-31 00:08:52

芝麻油排行榜(芝麻油排行榜最新)

2023-05-30 23:28:17

绿盟科技:预计会在今年三季度发布AI相关产品

2023-05-30 23:10:46

北京药品医疗器械创新服务站(亦庄站)揭牌启用-环球看点

2023-05-30 22:53:42

经济观察丨规范矿业权出让 四川打出“组合拳”

2023-05-30 22:04:36

共43门课程!第七批海南省高校精品在线开放课程名单公布 天天热文

2023-05-30 21:52:56

全球快报:南大光电(300346.SZ):公司开发了多款ArF光刻胶在下游客户处验证

2023-05-30 21:42:27

矩子科技(300802):完成工商变更登记并换发营业执照的公告|全球快消息

2023-05-30 21:22:48

碧水源副总经理刘建军辞职 2022年公司净利7.08亿-全球关注

2023-05-30 21:36:37

古韵新声 余音绕梁 ——南昌大学“钢琴与中国古诗词的邂逅”专场音乐会 天天热点

2023-05-30 21:01:58

国家发展改革委:5月30日24时起国内汽、柴油价格每吨分别提高100元和95元 全球头条

2023-05-30 20:37:12

上海华瑞银行:取消发行“23上海华瑞银行CD418”-每日热点

2023-05-30 20:30:31

你往哪里跑电影高清完整版(你往哪里跑) 全球新消息

2023-05-30 20:19:51

征和工业(003033.SZ)2022年度拟每10股派5元 6月7日除权除息

2023-05-30 20:21:25

【全球独家】厦门迎来今年首个高温日

2023-05-30 20:08:54

5月30日海右石化石油焦价格平稳

2023-05-30 19:33:43

全球观速讯丨国家知识产权局:2022年长三角地区专利质押融资金额达1862.6亿元

2023-05-30 19:07:25

2023郑州民办初中报名政策最新

2023-05-30 18:18:50

全球头条:在淄博,广汽埃安怎样升级的汽车音响,使音质提升3-5倍

2023-05-30 18:09:01

都是假的!美国一资深律师用ChatGPT协助办案被“坑”

2023-05-30 17:51:29

勤王敢道远,私向梦中归翻译_勤王敢道远_微动态

2023-05-30 17:26:47

暗黑破坏神2狱火重生(黑暗破坏神2)

2023-05-30 17:36:27

无锡好的男科医院是哪家呢{榜单公开}无锡博仁男科医院值得信赖

2023-05-30 17:24:49

环球观热点:许昌市前4个月一般公共预算收入完成76.5亿元,增长20.1%

2023-05-30 16:56:33

离职后与公司一切无关的声明怎么写_高以翔公司为了什么事情发紧急声明 每日精选

2023-05-30 16:37:36

国际乒联官宣。国乒世乒赛后再收喜讯,张本智和麻烦烦大了

2023-05-30 16:20:47

道外 政银企对接 解融资难题 全球热议

2023-05-30 15:40:27

天天头条:足金回收多少钱一克(2023年5月30日)

2023-05-30 15:44:33

2023年温州市洞头区面向社会公开招聘教师计划一览表

2023-05-30 15:11:54

10家基地拟入选首批省级非遗旅游体验基地

2023-05-30 14:55:17

意媒:帕普-戈麦斯为参加世界杯寻求巫师帮助,以队友受伤为代价 世界聚看点

2023-05-30 14:48:30

图集|梦回春秋:莒国古城再现古文化风韵

2023-05-30 14:12:25

安卓13QPR2Beta1中的新功能 环球焦点

2023-05-30 13:32:23

神舟十六号发射时间地点和返回时间 神舟十六号三名人员名单 今日视点

2023-05-30 12:52:39

CD Projekt一季度财报:利润增长1% 营收下滑19%

2023-05-30 12:17:53

天舟文化5月30日快速反弹

2023-05-30 11:25:45

中信银行合肥分行组织“党建+运营技能大PK”业务竞赛_聚焦

2023-05-30 10:29:38

蔚来nop是什么意思(汽车nop是什么意思?)_环球通讯

2023-05-30 09:39:19

世界热点评!每日新车:路特斯 TYPE 133 国内谍照;新款捷途 X70M 正式上市

2023-05-30 08:49:05

祁连山05月29日获沪股通增持67.03万股

2023-05-30 08:05:53

夏娃直播视频_夏娃的种子直播

2023-05-30 07:24:34

世界观速讯丨莱宝高科:已制作出SFI结构柔性触摸屏样品

2023-05-30 06:22:11

全球今日报丨大宗交易:中微公司成交1.17亿元,折价3.12%(05-29)

2023-05-30 05:53:01

艾比森品牌官宣:AbseniconP系列正式发布

2023-05-30 04:46:49

全球即时:如何当好饭店服务员_如何做好饭店服务员

2023-05-30 04:42:44

外汇百晓生:黄金震荡1950空单持有,等待明天下跌 实时焦点

2023-05-30 03:03:01

人为什么要眨眼完整版故事 人为什么要眨眼

2023-05-30 02:02:36

头痒怎么办最快最有效用什么洗头_头痒怎么办 滚动

2023-05-30 01:01:46

天天微动态丨非专利权人可以享有专利吗

2023-05-29 23:57:13

今日介绍一部好看的连续剧(有什么好看的连续剧介绍的)

2023-05-29 23:30:18

如何双冲咖啡_给女生送什么生日礼物好14岁的男生

2023-05-29 23:33:30

汇丰晋信基金副总经理兼首席运营官赵琳离任|天天速看料

2023-05-29 23:27:23

“爱甲行动” 信阳市中心医院肿瘤外科二病区开展“守护甲状腺,提升她力量”宣传科普活动

2023-05-29 21:20:44

荣耀90系列搭载创新动脉仿生VC:首发航天级导热凝胶

2023-05-29 20:54:12

【天天新要闻】雷凌刹车片多久换一次_雷凌空滤多久换一次?

2023-05-29 19:39:15

江苏出台金融支持制造业发展若干意见 要求避免盲目抽贷、断贷、压贷

2023-05-29 19:02:31

厦门工业博览会同期展会2023 天天观察

2023-05-29 17:58:29

客家汤饼_关于客家汤饼介绍

2023-05-29 17:21:34

新加坡总统哈莉玛发文 称决定不参加下届总统选举 世界关注

2023-05-29 17:08:18

慢综艺返璞归真更打动人心

2023-05-29 16:37:11

德班世乒赛大结局:中国再现统治地位,韩国超越日本,非洲获惊喜

2023-05-29 15:46:18

最新快讯!坑完粉丝 3000 万,财经大佬去吃牢饭了

2023-05-29 14:28:36

一季度承运农产品运输量同比增长61.82%,满帮“黔货出山”交流会在数博大厅举办-世界速读

2023-05-29 13:32:10

传闻中的陈芊芊原著小说作者(传闻中的陈芊芊的原著小说) 全球快播报

2023-05-29 12:59:45

焦点观察:各地三夏抢收进度如何?一组数字看中国丰收“速度”

2023-05-29 12:08:03

“蚌”发青春 就业引航——蚌埠市招才引智高校行再出发_焦点快报

2023-05-29 11:41:00

“五个一办”跑出便民“加速度”|贵阳市政务服务事项时限压缩率达82.95%

2023-05-29 11:02:32

美联储古尔斯比:美国达成债务上限协议避免了“极其负面”的后果-视点

2023-05-29 09:56:41

全球热门:郑州:本周多云天气为主周初和周末有阵雨

2023-05-29 09:16:10

静安区全民健身中心项目进入实质性施工阶段

2023-05-29 08:22:11

咪咕nba篮球现场直播_中央5篮球现场直播_每日精选

2023-05-29 07:17:07

突发!彩排时背景屏倒塌,9名幼儿被砸伤 快播

2023-05-29 06:24:05

全球快消息!熟牛肉凉拌还是直接吃好 熟牛肉凉拌还是直接食用更加好吃呢

2023-05-29 05:03:48

晴好天气接近尾声!未来三天全省多雨~|世界短讯

2023-05-29 03:23:29

亚洲面积最大的国家和最小的国家 亚洲面积最大的国家-当前快报

2023-05-29 02:03:29