Redis事件管理(二)
Redis的定时器是自己实现的,不是很复杂。说说具体的实现吧。
定时器的存储维护采用的是普通的单向链表结构,具体节点定义为:
1 /*时间定时器结构体*/ 2 typedef struct aeTimeEvent 3 { 4 long long id; /*定时器的编号*/ 5 long when_sec; /* seconds */ 6 long when_ms; /* milliseconds */ 7 aeTimeProc *timeProc;/*时间到达处理函数*/ 8 aeEventFinalizerProc *finalizerProc;/*删除清理函数*/ 9 void *clientData;/*带外数据*/ 10 struct aeTimeEvent *next;/*定时器的存储采用的是链表结构*/ 11 } aeTimeEvent;
定时器记录的根节点的位置是在事件管理器中,剩下的就是普通的操作函数了
1 /*添加定时器事件,参数为时间控制器,定时时间,处理函数,函数参数,清理函数*/ 2 long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds, 3 aeTimeProc *proc, void *clientData, 4 aeEventFinalizerProc *finalizerProc) 5 { 6 /*生成ID*/ 7 long long id = eventLoop->timeEventNextId++; 8 aeTimeEvent *te; 9 10 te = zmalloc(sizeof(*te)); 11 if (te == NULL) return AE_ERR; 12 te->id = id; 13 /*调整时间*/ 14 aeAddMillisecondsToNow(milliseconds,&te->when_sec,&te->when_ms); 15 te->timeProc = proc; 16 te->finalizerProc = finalizerProc; 17 te->clientData = clientData; 18 /*加入时间链表中,所有的时间节点都是在头部插入的,没顺序*/ 19 te->next = eventLoop->timeEventHead; 20 eventLoop->timeEventHead = te; 21 return id; 22 }
1 /*删除时间节点,提供时间点的ID就好,如果没找到就返回错误*/ 2 int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id) 3 { 4 aeTimeEvent *te, *prev = NULL; 5 6 te = eventLoop->timeEventHead; 7 /*循环查找节点,调用清理函数并释放内存*/ 8 while(te) 9 { 10 if (te->id == id) 11 { 12 if (prev == NULL) 13 eventLoop->timeEventHead = te->next; 14 else 15 prev->next = te->next; 16 if (te->finalizerProc) 17 te->finalizerProc(eventLoop, te->clientData); 18 zfree(te); 19 return AE_OK; 20 } 21 prev = te; 22 te = te->next; 23 } 24 return AE_ERR; /* NO event with the specified ID found */ 25 }
1 /*查找激活时间最短的节点*/ 2 static aeTimeEvent *aeSearchNearestTimer(aeEventLoop *eventLoop) 3 { 4 aeTimeEvent *te = eventLoop->timeEventHead; 5 aeTimeEvent *nearest = NULL; 6 7 while(te) 8 { 9 if (!nearest || te->when_sec < nearest->when_sec || (te->when_sec == nearest->when_sec && te->when_ms < nearest->when_ms)) 10 nearest = te; 11 te = te->next; 12 } 13 return nearest; 14 }
1 /* Process time events 处理时间事件,先执行定时器事件,如果有时间设置过端的情况,会导致整个卡死*/ 2 static int processTimeEvents(aeEventLoop *eventLoop) 3 { 4 int processed = 0; 5 aeTimeEvent *te; 6 long long maxId; 7 time_t now = time(NULL); 8 9 if (now < eventLoop->lastTime) /*为啥会出现当前时间小于上次激活时间的问题,重新设置了系统时间,当前时间小于了最后的插入时间*/ 10 { 11 te = eventLoop->timeEventHead; 12 while(te) 13 { 14 te->when_sec = 0; 15 te = te->next; 16 } 17 } 18 eventLoop->lastTime = now;/*更新最后操作时间*/ 19 20 te = eventLoop->timeEventHead; 21 maxId = eventLoop->timeEventNextId-1; 22 /*时间处理,从链表中逐个检查,找到到达时间的定时器后调用处理函数,然后重新从头遍历,当某个定时器的*/ 23 while(te) 24 { 25 long now_sec, now_ms; 26 long long id; 27 28 if (te->id > maxId) 29 { 30 te = te->next; 31 continue; 32 } 33 aeGetTime(&now_sec, &now_ms); 34 if (now_sec > te->when_sec || (now_sec == te->when_sec && now_ms >= te->when_ms)) 35 {/*当该节点时间到达时,调用时间处理函数,根据函数的返回来决定是否需要删除定时节点*/ 36 int retval; 37 38 id = te->id; 39 retval = te->timeProc(eventLoop, id, te->clientData); 40 processed++; 41 42 /*根据函数的返回值来确定是否仍需注册,返回值为下次的激活时间*/ 43 if (retval != AE_NOMORE) 44 { 45 /*返回值不为-1时需要重新注册函数,返回值为下次激活的时间*/ 46 aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms); 47 } 48 else 49 { 50 /*删除定时器*/ 51 aeDeleteTimeEvent(eventLoop, id); 52 } 53 /*定时器函数处理完毕之后为了防止漏掉节点,重新从头结点遍历,这个地方要注意,如果定时时间太短,这个地方会造成死循环,不停的处理定时器事件*/ 54 te = eventLoop->timeEventHead; 55 } 56 else 57 { 58 te = te->next; 59 } 60 } 61 return processed; 62 }