Redis定时器实现原理
1.一个循环体循环检查一个链表
2.链表中的节点主要包含时间戳,回调函数
3.每次循环获取当前系统时间,并与链表节点的时间戳对比
4.如果差值大于时间阈值,则执行链表节点的回调函数,并用当前系统时间更新链表节点的时间戳,然后进入下次循环
Redis定时器实现包含以下几个主要函数
1.一个主循环体aeMain,aeMain不光是定时器还包含其它事件处理,都在这个while中处理
void aeMain(aeEventLoop *eventLoop) {
eventLoop->stop = 0;
while (!eventLoop->stop) {
if (eventLoop->beforesleep != NULL)
eventLoop->beforesleep(eventLoop);
aeProcessEvents(eventLoop, AE_ALL_EVENTS|AE_CALL_AFTER_SLEEP);
}
}
2.循环指定的内容,aeProcessEvents中包含了其它事件处理机制,这里只看定时器处理相关的代码
int aeProcessEvents(aeEventLoop *eventLoop, int flags)
{
…………
/* Check time events */
if (flags & AE_TIME_EVENTS)
processed += processTimeEvents(eventLoop);
…………
}
3.processTimeEvents遍历链表,如果时间到了,执行链表节点的回调函数,并更新节点时间戳,进入下次循环
/* Process time events */
static int processTimeEvents(aeEventLoop *eventLoop) {
int processed = 0;
aeTimeEvent *te;
long long maxId;
time_t now = time(NULL);
/* If the system clock is moved to the future, and then set back to the
* right value, time events may be delayed in a random way. Often this
* means that scheduled operations will not be performed soon enough.
*
* Here we try to detect system clock skews, and force all the time
* events to be processed ASAP when this happens: the idea is that
* processing events earlier is less dangerous than delaying them
* indefinitely, and practice suggests it is. */
//如果时间异常,当前时间比最后一次更新时间还要早,那将所有定时器都刷新要需要处理的状态
//避免定时器长时间得不到处理
if (now < eventLoop->lastTime) {
te = eventLoop->timeEventHead;
while(te) {
te->when_sec = 0;
te = te->next;
}
}
//更新一下定时器最后一次处理时间
eventLoop->lastTime = now;
//取定时器头
te = eventLoop->timeEventHead;
//maxId 代表当前定时器链表的节点数量
maxId = eventLoop->timeEventNextId-1;
while(te) {
long now_sec, now_ms;
long long id;
/* Remove events scheduled for deletion. */
//如果定时器标记为删除执行finalizerProc,并释放该节点
if (te->id == AE_DELETED_EVENT_ID) {
aeTimeEvent *next = te->next;
if (te->prev)
te->prev->next = te->next;
else
eventLoop->timeEventHead = te->next;
if (te->next)
te->next->prev = te->prev;
if (te->finalizerProc)
te->finalizerProc(eventLoop, te->clientData);
zfree(te);
te = next;
continue;
}
/* Make sure we don't process time events created by time events in
* this iteration. Note that this check is currently useless: we always
* add new timers on the head, however if we change the implementation
* detail, this check may be useful again: we keep it here for future
* defense. */
if (te->id > maxId) {
te = te->next;
continue;
}
aeGetTime(&now_sec, &now_ms);
//如果当前时间超过了定时器设置的when,执行定时器的timeProc函数,如serverCron
//timeProc执行后如果以后不需要执行了
//返回AE_NOMORE,则给该定时器赋值DELETE状态,下次循环会删除该节点
//如果后续还需要继续执行,则返回下次执行的时间间隔,aeAddMillisecondsToNow在
//when_sec\when_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.链表节点,主要包含时间戳、回调函数,回收函数
/* Types and data structures */
typedef void aeFileProc(struct aeEventLoop *eventLoop, int fd, void *clientData, int mask);
typedef int aeTimeProc(struct aeEventLoop *eventLoop, long long id, void *clientData);
typedef void aeEventFinalizerProc(struct aeEventLoop *eventLoop, void *clientData);
typedef void aeBeforeSleepProc(struct aeEventLoop *eventLoop);
/* Time event structure */
typedef struct aeTimeEvent {
long long id; /* time event identifier. */
//需要执行的时刻 when = current + time
long when_sec; /* seconds */
long when_ms; /* milliseconds */
aeTimeProc *timeProc;
aeEventFinalizerProc *finalizerProc;
void *clientData;
struct aeTimeEvent *prev;
struct aeTimeEvent *next;
} aeTimeEvent;
2.创建一个定时器,并注册其回调函数
int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData)
/* Create the timer callback, this is our way to process many background
* operations incrementally, like clients timeout, eviction of unaccessed
* expired keys and so forth. */
if (aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) {
serverPanic("Can't create event loop timers.");
exit(1);
}
在aeCreateTimeEvent中申请节点,用回调函数指针赋值节点,并将节点添加到链表中,循环体循环检查链表
long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,
aeTimeProc *proc, void *clientData,
aeEventFinalizerProc *finalizerProc)
{
//产生一个aeTimeEvent结构中的事件id
long long id = eventLoop->timeEventNextId++;
aeTimeEvent *te;
//申请一个新的aeTimeEvent结构
te = zmalloc(sizeof(*te));
if (te == NULL) return AE_ERR;
te->id = id;
//取当前时间
aeAddMillisecondsToNow(milliseconds,&te->when_sec,&te->when_ms);
//需要定时执行的函数,比如serverCron
te->timeProc = proc;
//删除定时器时执行finalizerProc
//serverCron永久定时器注册时finalizerProc为NULL
te->finalizerProc = finalizerProc;
//serverCron注册时clientData为NULL
te->clientData = clientData;
//将新增的aeTimeEvent添加到表头
te->prev = NULL;
te->next = eventLoop->timeEventHead;
if (te->next)
te->next->prev = te;
eventLoop->timeEventHead = te;
return id;
}