redis03 补充 redis驱动模型:事件驱动
1.文件事件(重点)文件事件就是服务器对socket操作的抽象,Redis服务器通过监听并处理这些socket产生的文件事件,实现对客户端调用的响应
1.1 文件事务处理器的构成
1.2 IO多路复用
注:
epoll是linux系统的底层IO多路复用技术
kqueue是mac系统的底层IO多路复用技术
redis对这些函数进行了封装,形成一个ae.c的文件,这个文件掩盖了不同操作系统底层的差异。Redis封装的接口定义如下:
typedef struct aeApiState {// epoll实例描述符int epfd;// 绑定的事件struct epoll_event *events;
} aeApiState;/*** 创建epoll */
static int aeApiCreate(aeEventLoop *eventLoop)/*** 调整绑定事件的大小*/
static int aeApiResize(aeEventLoop *eventLoop, int setsize)/*** 释放epoll*/
static void aeApiFree(aeEventLoop *eventLoop)/*** 绑定新事件*/
static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask)/*** 删除事件*/
static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask)/*** 获取可执行事件*/
static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp)
在linux中,一般使用 Epoll 中,Epoll 就是事件通知器,可以向 Epoll 注册我们感兴趣的事件。一般这里的IO多路复用技术可以认为是Epoll,Epoll会把事件传递到 事件分发器
1.3事务分发器
事务分发器一般有两种,一种是文件分发器,一种是时间分发器。这里讲的是文件事件,所以一般用文件事务分发器来描述:
文件事件分发的事件分发器
ae.c中aeProcessEvents实现了事件分发器的功能,文件事件分发的核心代码如下:
// I/O多路复用,当有事件发生或者超时才返回
// 调用操作系统函数实现,在Linux上为epoll
numevents = aeApiPoll(eventLoop, tvp);/* After sleep callback. */
if (eventLoop->aftersleep != NULL && flags & AE_CALL_AFTER_SLEEP)eventLoop->aftersleep(eventLoop);// 遍历发生的事件
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 fired = 0;int invert = fe->mask & AE_BARRIER;// 处理读事件if (!invert && fe->mask & mask & AE_READABLE) {fe->rfileProc(eventLoop,fd,fe->clientData,mask);fired++;}// 处理写事件if (fe->mask & mask & AE_WRITABLE) {if (!fired || fe->wfileProc != fe->rfileProc) {fe->wfileProc(eventLoop,fd,fe->clientData,mask);fired++;}}// 如果颠倒,那么此时处理读事件if (invert && fe->mask & mask & AE_READABLE) {if (!fired || fe->wfileProc != fe->rfileProc) {fe->rfileProc(eventLoop,fd,fe->clientData,mask);fired++;}}processed++;
}
时间事件处理的核心代码如下:
static int processTimeEvents(aeEventLoop *eventLoop) {int processed = 0;aeTimeEvent *te;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;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 (te->prev)te->prev->next = te->next;elseeventLoop->timeEventHead = te->next;if (te->next)te->next->prev = te->prev;if (te->finalizerProc)te->finalizerProc(eventLoop, te->clientData);zfree(te);te = next;continue;}// 如果时间事件ID大于maxId,说明该时间事件是本次遍历过程中产生的,本次遍历不处理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;}}// 继续下一个时间事件,直到链表结束te = te->next;}return processed;
}
1.4 文件事件的结构
typedef struct aeFileEvent {// 监听事件类型掩码,可表示为事件类型// 值可以是 AE_READABLE 或 AE_WRITABLE ,// 或者 AE_READABLE | AE_WRITABLEint mask; /* one of AE_(READABLE|WRITABLE) */// 读事件处理器aeFileProc *rfileProc;// 写事件处理器aeFileProc *wfileProc;// 多路复用库的私有数据void *clientData;
} aeFileEvent;// 文件事件处理器
typedef void aeFileProc(struct aeEventLoop *eventLoop, int fd, void *clientData, int mask);
1.5 主要是第一个函数createFileEvent,用于绑定文件事件
int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,aeFileProc *proc, void *clientData)
{// 校验fdif (fd >= eventLoop->setsize) {errno = ERANGE;return AE_ERR;}// 准备填充该FD对应的处理aeFileEvent *fe = &eventLoop->events[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;if (fd > eventLoop->maxfd)eventLoop->maxfd = fd;return AE_OK;
}
1.6最终事件处理器来执行事件
1.6.1
1.6.2
1.6.31.6.4
2.时间事件
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 *prev;// 指向下个时间事件结构,形成链表struct aeTimeEvent *next;
} aeTimeEvent;// 时间事件处理器
typedef int aeTimeProc(struct aeEventLoop *eventLoop, long long id, void *clientData);
// 时间事件终结处理器
typedef void aeEventFinalizerProc(struct aeEventLoop *eventLoop, void *clientData);
举个例子:
说明一下,链表是无序的
此时的redis一般都只有一个时间事件