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;
}//撤销时间事件的监听,从时间事件链表中删除aeTimeEvent
int 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);
}
}