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 }