Redis Reactor事件驱动模型源码

  前置学习:Redis server启动源码-CSDN博客

 Redis采用单线程Reactor模型

三个关键角色,即 reactor、acceptor、handler

三类处理事件,即连接事件、写事件、读事件。

建立连接(Acceptor)、监听accept、read、write事件(Reactor)、处理事件(Handler)

概念性的东西,实际简单一些,看源码简单些,定义事件、事件监听、事件分发处理。

注册事件

aeCreateEventLoop

初始化EventLoop,添加文件事件源、已就绪文件事件源、epoll事件源。

注册文件事件源、已就绪文件事件源
1、定义文件事件结构和已就绪事件结构
/* File event structure** 文件事件结构*/
typedef struct aeFileEvent {// 监听事件类型掩码,// 值可以是 AE_READABLE 或 AE_WRITABLE ,// 或者 AE_READABLE | AE_WRITABLEint mask; /* one of AE_(READABLE|WRITABLE) */// 读事件处理器aeFileProc *rfileProc;// 写事件处理器aeFileProc *wfileProc;// 多路复用库的私有数据void *clientData;} aeFileEvent;
/* A fired event** 已就绪事件*/
typedef struct aeFiredEvent {// 已就绪文件描述符int fd;// 事件类型掩码,// 值可以是 AE_READABLE 或 AE_WRITABLE// 或者是两者的或int mask;} aeFiredEvent;
2、初始化事件源(EventLoop),并将文件事件结构和已就绪事件结构加入事件源中。
/* State of an event based program ** 事件处理器的状态*/
typedef struct aeEventLoop {// 目前已注册的最大描述符int maxfd;   /* highest file descriptor currently registered */// 目前已追踪的最大描述符int setsize; /* max number of file descriptors tracked */// 用于生成时间事件 idlong long timeEventNextId;// 最后一次执行时间事件的时间time_t lastTime;     /* Used to detect system clock skew */// 已注册的文件事件aeFileEvent *events; /* Registered events */// 已就绪的文件事件aeFiredEvent *fired; /* Fired events */// 时间事件aeTimeEvent *timeEventHead;// 事件处理器的开关int stop;// 多路复用库的私有数据void *apidata; /* This is used for polling API specific data */// 在处理事件前要执行的函数aeBeforeSleepProc *beforesleep;} aeEventLoop;/** 初始化事件处理器状态*/
aeEventLoop *aeCreateEventLoop(int setsize) {aeEventLoop *eventLoop;int i;aeMain// 创建事件状态结构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;/* Events with mask == AE_NONE are not set. So let's initialize the* vector with it. */// 初始化监听事件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;
}
 注册Epoll事件
 1、定义Epoll事件结构体
/** 事件状态*/
typedef struct aeApiState {// epoll_event 实例描述符int epfd;// 事件槽struct epoll_event *events;} aeApiState;
2、添加epoll事件源到EventLoop中
/** 创建一个新的 epoll 实例,并将它赋值给 eventLoop*/
static int aeApiCreate(aeEventLoop *eventLoop) {aeApiState *state = zmalloc(sizeof(aeApiState));if (!state) return -1;// 初始化事件槽空间state->events = zmalloc(sizeof(struct epoll_event)*eventLoop->setsize);if (!state->events) {zfree(state);return -1;}// 创建 epoll 实例state->epfd = epoll_create(1024); /* 1024 is just a hint for the kernel */if (state->epfd == -1) {zfree(state->events);zfree(state);return -1;}// 赋值给 eventLoopeventLoop->apidata = state;return 0;
}

aeCreateFileEvent

初始化事件回调,往Eventloop里面添加读事件的处理回调,写事件的处理回调,Epoll事件的处理回调。

/** 根据 mask 参数的值,监听 fd 文件的状态,* 当 fd 可用时,执行 proc 函数*/
int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,aeFileProc *proc, void *clientData)
{if (fd >= eventLoop->setsize) {errno = ERANGE;return AE_ERR;}if (fd >= eventLoop->setsize) return AE_ERR;// 取出文件事件结构aeFileEvent *fe = &eventLoop->events[fd];// 监听指定 fd 的指定事件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;// 如果有需要,更新事件处理器的最大 fdif (fd > eventLoop->maxfd)eventLoop->maxfd = fd;return AE_OK;
}static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {aeApiState *state = eventLoop->apidata;struct epoll_event ee;/* If the fd was already monitored for some event, we need a MOD* operation. Otherwise we need an ADD operation. ** 如果 fd 没有关联任何事件,那么这是一个 ADD 操作。** 如果已经关联了某个/某些事件,那么这是一个 MOD 操作。*/int op = eventLoop->events[fd].mask == AE_NONE ?EPOLL_CTL_ADD : EPOLL_CTL_MOD;// 注册事件到 epollee.events = 0;mask |= eventLoop->events[fd].mask; /* Merge old events */if (mask & AE_READABLE) ee.events |= EPOLLIN;if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;ee.data.u64 = 0; /* avoid valgrind warning */ee.data.fd = fd;if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1;return 0;
}

aeCreateTimeEvent 

1、定义时间事件结构
/* Time event structure** 时间事件结构*/
typedef struct aeTimeEvent {// 时间事件的唯一标识符long long id; /* time event identifier. */// 事件的到达时间long when_sec; /* seconds */long when_ms; /* milliseconds */// 事件处理函数aeTimeProc *timeProc;// 事件释放函数aeEventFinalizerProc *finalizerProc;// 多路复用库的私有数据void *clientData;// 指向下个时间事件结构,形成链表struct aeTimeEvent *next;} aeTimeEvent;
2、往EventLoop添加事件源回调处理方法。 

/** 创建时间事件*/
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;// 设置 IDte->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;
}

事件分发

执行EventLoop事件源里面的所有事件回调

/* Process every pending time event, then every pending file event* (that may be registered by time event callbacks just processed).** 处理所有已到达的时间事件,以及所有已就绪的文件事件。** Without special flags the function sleeps until some file event* fires, or when the next time event occurs (if any).** 如果不传入特殊 flags 的话,那么函数睡眠直到文件事件就绪,* 或者下个时间事件到达(如果有的话)。** If flags is 0, the function does nothing and returns.* 如果 flags 为 0 ,那么函数不作动作,直接返回。** if flags has AE_ALL_EVENTS set, all the kind of events are processed.* 如果 flags 包含 AE_ALL_EVENTS ,所有类型的事件都会被处理。** if flags has AE_FILE_EVENTS set, file events are processed.* 如果 flags 包含 AE_FILE_EVENTS ,那么处理文件事件。** if flags has AE_TIME_EVENTS set, time events are processed.* 如果 flags 包含 AE_TIME_EVENTS ,那么处理时间事件。** if flags has AE_DONT_WAIT set the function returns ASAP until all* the events that's possible to process without to wait are processed.* 如果 flags 包含 AE_DONT_WAIT ,* 那么函数在处理完所有不许阻塞的事件之后,即刻返回。** The function returns the number of events processed. * 函数的返回值为已处理事件的数量*/
int aeProcessEvents(aeEventLoop *eventLoop, int flags)
{int processed = 0, numevents;/* Nothing to do? return ASAP */if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0;/* Note that we want call select() even if there are no* file events to process as long as we want to process time* events, in order to sleep until the next time event is ready* to fire. */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);if (shortest) {// 如果时间事件存在的话// 那么根据最近可执行时间事件和现在时间的时间差来决定文件事件的阻塞时间long now_sec, now_ms;/* Calculate the time missing for the nearest* timer to fire. */// 计算距今最近的时间事件还要多久才能达到// 并将该时间距保存在 tv 结构中aeGetTime(&now_sec, &now_ms);tvp = &tv;tvp->tv_sec = shortest->when_sec - now_sec;if (shortest->when_ms < now_ms) {tvp->tv_usec = ((shortest->when_ms+1000) - now_ms)*1000;tvp->tv_sec --;} else {tvp->tv_usec = (shortest->when_ms - now_ms)*1000;}// 时间差小于 0 ,说明事件已经可以执行了,将秒和毫秒设为 0 (不阻塞)if (tvp->tv_sec < 0) tvp->tv_sec = 0;if (tvp->tv_usec < 0) tvp->tv_usec = 0;} else {// 执行到这一步,说明没有时间事件// 那么根据 AE_DONT_WAIT 是否设置来决定是否阻塞,以及阻塞的时间长度/* If we have to check for events but need to return* ASAP because of AE_DONT_WAIT we need to set the timeout* to zero */if (flags & AE_DONT_WAIT) {// 设置文件事件不阻塞tv.tv_sec = tv.tv_usec = 0;tvp = &tv;} else {/* Otherwise we can block */// 文件事件可以阻塞直到有事件到达为止tvp = NULL; /* wait forever */}}// 处理文件事件,阻塞时间由 tvp 决定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;/* note the fe->mask & mask & ... code: maybe an already processed* event removed an element that fired and we still didn't* processed, so we check if the event is still valid. */// 读事件if (fe->mask & mask & AE_READABLE) {// rfired 确保读/写事件只能执行其中一个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++;}}/* Check time events */// 执行时间事件if (flags & AE_TIME_EVENTS)processed += processTimeEvents(eventLoop);return processed; /* return the number of processed file/time events */
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/214201.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

测试命题 cuda kernel 和 cudaMemcpy 是异步执行

前置命题&#xff0c;保序的命题&#xff1a; 同一个任意的stream中的gpu操作&#xff08;memcpy和kernel&#xff09;&#xff0c;在gpu内部都是严格保序的&#xff0c;即&#xff0c;前一个gpu任务结束后才会执行下一个任务。 测试两个命题&#xff1a; 1&#xff0c;cuda …

python画动漫形象(魔法少女小圆晓美焰,super beautiful)

1.源代码 import turtle as te import time WriteStep 15 # 贝塞尔函数的取样次数 Speed 5 Width 600 # 界面宽度 Height 500 # 界面高度 Xh 0 # 记录前一个贝塞尔函数的手柄 Yh 0 def Bezier(p1, p2, t): # 一阶贝塞尔函数 return p1 * (1 - t) p2 * t def Bezier_2(x1…

JVM 性能调优及监控诊断工具 jps、jstack、jmap、jhat、jstat、hprof 使用详解

目录 一. 前言 二. jps&#xff08;Java Virtual Machine Process Status Tool&#xff09; 三. jstack 四. jmap&#xff08;Memory Map&#xff09;和 jhat&#xff08;Java Heap Analysis Tool&#xff09; 五. jstat&#xff08;JVM统计监测工具&#xff09; 六. hpro…

MySQL系列(一):索引篇

为什么是B树&#xff1f; 我们推导下&#xff0c;首先看下用哈希表做索引&#xff0c;是否可以满足需求。如果我们用哈希建了索引&#xff0c;那么对于如下这种SQL&#xff0c;通过哈希&#xff0c;可以快速检索出数据&#xff1a; select * from t_user_info where id1;但是这…

logback日志框架使用

依赖引入 <dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.1.7</version> </dependency> 使用logback日志框架只需要引入以上即可&#xff0c;(我们平时使用较多的Slf4j…

Python爬虫-实现批量抓取王者荣耀皮肤图片并保存到本地

前言 本文是该专栏的第12篇,后面会持续分享python爬虫案例干货,记得关注。 本文以王者荣耀的英雄皮肤为例,用python实现批量抓取“全部英雄”的皮肤图片,并将图片“批量保存”到本地。具体实现思路和详细逻辑,笔者将在正文结合完整代码进行详细介绍。注意,这里抓取的图片…

SpringMVC修炼之旅(3)REST风格与拦截器

一、概述 1.1简介 Restful就是一个资源定位及资源操作的风格。不是标准也不是协议&#xff0c;只是一种风格。基于这个风格设计的软件可以更简洁&#xff0c;更有层次&#xff0c;更易于实现缓存等机制。 1.2功能 资源&#xff1a;互联网所有的事物都可以被抽象为资源 资源操作…

ELK 日志解决方案

ELK 是目前最流行的集中式日志解决方案&#xff0c;提供了对日志收集、存储、展示等一站式的解决方案。 ELK 分别指 Elasticsearch、Logstash、Kibana。 Elasticsearch&#xff1a;分布式数据搜索引擎&#xff0c;基于 Apache Lucene 实现&#xff0c;可集群&#xff0c;提供…

本地搭建Linux DataEase数据可视化分析工具并实现公网访问

文章目录 前言1. 安装DataEase2. 本地访问测试3. 安装 cpolar内网穿透软件4. 配置DataEase公网访问地址5. 公网远程访问Data Ease6. 固定Data Ease公网地址 前言 DataEase 是开源的数据可视化分析工具&#xff0c;帮助用户快速分析数据并洞察业务趋势&#xff0c;从而实现业务…

软件工程复习

一、题型 单项选择题 20分 填空题 10分 判断题 10分 简答题 18分 应用题 12分 综合题 30分 软件程序数据文档 软件是无形的、不可见的逻辑实体 20世纪60年代末爆发软件危机 软件危机是指软件在开发与维护过程中遇到的一系列严重的问题 …

Mac安装Anaconda3最新实用教程

Anaconda3安装 1、Anaconda3下载 我用的是这个链接&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ 可以按需要选择自己需要的版本&#xff0c;也可以自行搜索其他网站下载 下载完成之后一路默认安装就可以了。 安装好之后可以在终端试一下&#xff1a;…

JS基础之原型原型链

JS基础之原型&原型链 原型&原型链构造函数创建对象prototypeprotoconstructor实例与原型原型的原型原型链其他constructorproto继承 原型&原型链 构造函数创建对象 我们先使用构造函数创建一个对象&#xff1a; function Person(){ } var person new Person();…

计网实验7

解决&#xff1a;路由器用rip连接&#xff0c;主机通过域名访问&#xff0c;主机之间发送电子邮件 实验步骤 1.搞好部件 2.配好两台主机的ip,掩码&#xff0c;网关 3.连接一下两台主机&#xff0c;由于两台路由器没有连接&#xff0c;所以两台主机也无法连通&#xff0c;丢包率…

[Linux] nginx防盗链与优化

一、Nginx的页面优化 1.1 Nginx的网页压缩 在Nginx的ngx_http_gzip_module压缩模块提供对文件内容压缩的功能。进行相关的配置修改&#xff0c;就能实现Nginx页面的压缩&#xff0c;达到节约带宽&#xff0c;提升用户访问速度 vim /usr/local/nginx/conf/nginx.conf http { .…

web前端之css变量的妙用、通过JavaScrip改变css文件中的属性值、querySelector、setProperty

MENU 效果图htmlJavaScripstylequerySelectorsetProperty 效果图 html <div id"idBox" class"p_r w_680 h_160 b_1s_red"><div id"idItem" class"p_a l_0 t_30 w_100 h_100 bc_rgba_255_00_05 radius_50_"></div> …

HarmonyOS Developer——鸿蒙【构建第一个JS应用(FA模型)】

创建JS工程 JS工程目录结构 构建第一个页面 构建第二个页面 实现页面间的跳转 使用真机运行应用 说明 为确保运行效果&#xff0c;本文以使用DevEco Studio 3.1 Release版本为例&#xff0c;点击此处获取下载链接。 创建JS工程 若首次打开DevEco Studio&#xff0c;请点击…

排序算法-选择/堆排序(C语言)

1基本思想&#xff1a; 每一次从待排序的数据元素中选出最小&#xff08;或最大&#xff09;的一个元素&#xff0c;存放在序列的起始位置&#xff0c;直到全部待排序的 数据元素排完 。 2 直接选择排序: 在元素集合 array[i]--array[n-1] 中选择关键码最大 ( 小 ) 的数据元素…

《PySpark大数据分析实战》图书上线啦

《PySpark大数据分析实战》图书上线啦 《PySpark大数据分析实战》图书上线啦特殊的日子关于创作关于数据关于Spark关于PySpark关于图书/专栏 《PySpark大数据分析实战》图书上线啦 特殊的日子 不知不觉一转眼入驻CSDN已经满一年了&#xff0c;这真是一个充满意义的特殊的日子&…

《python每天一小段》--12 数据可视化《1》

欢迎阅读《Python每天一小段》系列&#xff01;在本篇中&#xff0c;将使用Python Matplotlib实现数据可视化的简单图形。 文章目录 一、概念&#xff08;1&#xff09;安装matplotlib&#xff08;2&#xff09;数据可视化实现步骤 二、绘制简单的折线图&#xff08;1&#xff…

mysql中NULL值

mysql中NULL值表示“没有值”&#xff0c;它跟空字符串""是不同的 例如&#xff0c;执行下面两个插入记录的语句&#xff1a; insert into test_table (description) values (null); insert into test_table (description) values ();执行以后&#xff0c;查看表的…