redis ae事件驱动的源码分析
来源:互联网 发布:顶级域名cc 编辑:程序博客网 时间:2024/06/05 09:12
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;}//撤销时间事件的监听,从时间事件链表中删除aeTimeEventint 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); }}
0 0
- redis ae事件驱动的源码分析
- Redis ae事件驱动源码分析
- Redis源码分析(二十)--- ae事件驱动
- redis的ae事件分析
- Redis源码分析(二十)——事件ae
- Redis源码分析笔记5-事件处理组件AE
- Redis源代码分析之七:事件驱动库分析——Ae
- redis源码分析(五)- Redis 事件驱动
- 结合redis设计与实现的redis源码学习-14-事件(ae.c/ae_epoll.c)
- Redis:ae事件模型
- Redis源码与事件驱动
- Nginx源码分析-事件驱动的初始化
- Nginx源码分析--事件驱动的初始化
- Nginx源码分析-事件驱动的初始化
- Redis网络库源码分析(3)之ae.c
- redis的事件驱动
- Redis AE 异步事件模块
- redis源码笔记-ae.c
- WebRTC音频处理流程概述
- 关于sublime默认打开浏览器的问题
- 文件读取的综合案例
- 密码不回显
- http协议之response案例二:定时刷新页面或跳转到其他页面
- redis ae事件驱动的源码分析
- 二叉树的序列化和反序列化java借助队列实现
- 【Ubuntu】更新系统时出现Hash校验和不符的错误(已解决)
- iOS企业版APP发布与更新
- CF Good Bye 2016 C New Year and Rating 模拟乱搞
- 组件图(构件图)
- Java堆、栈和常量池
- Android开发动态二维数组赋值
- 55. Jump Game