redis中的事件分为文件事件和时间事件。redis中用aeEventLoop 来记录事件的状态
typedef struct aeEventLoop {
// 文件事件
aeFileEvent *events; /* Registered events */
// 时间事件
aeTimeEvent *timeEventHead;
} aeEventLoop;
我们所说的文件事件和时间事件也是作为结构的成员变量保存在aeEventLoop中
其中保存文件事件的结构体变量如下:
typedef struct aeFileEvent {
// 监听事件类型掩码,
int mask; /* one of AE_(READABLE|WRITABLE) */
// 读事件的回调函数
aeFileProc *rfileProc;
// 写事件的回调函数
aeFileProc *wfileProc;
// 读事件和写事件的形参
void *clientData;
} aeFileEvent;
表示时间事件的结构体如下所示:
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 *next;
} aeTimeEvent;
redis时间处理的策略可以分为下面四种
#ifdef HAVE_EVPORT
#include "ae_evport.c"
#else
#ifdef HAVE_EPOLL
#include "ae_epoll.c"
#else
#ifdef HAVE_KQUEUE
#include "ae_kqueue.c"
#else
#include "ae_select.c"
#endif
#endif
#endif
这四种根据编译时选择哪个宏来决定,选择不同的策略性能可能会有区别,理论性能从大到小分别是HAVE_EVPORT > HAVE_EPOLL > HAVE_KQUEUE > select(默认)
redis的事件处理的函数如下:
void aeMain(aeEventLoop *eventLoop) {
eventLoop->stop = 0;
// 可见处理事件的是一个死循环
while (!eventLoop->stop) {
// 在时间处理之前可以调用sleep函数
if (eventLoop->beforesleep != NULL)
eventLoop->beforesleep(eventLoop);
// 开始处理时间和文件事件
aeProcessEvents(eventLoop, AE_ALL_EVENTS);
}
}
int aeProcessEvents(aeEventLoop *eventLoop, int flags)
{
int processed = 0, numevents;
/* Nothing to do? return ASAP */
// 如果目前没有时间和文件事件,则直接返回0
if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0;
/* Note that we want call select() even if there are no
* file events to process as long as we want to process time
* events, in order to sleep until the next time event is ready
* to fire. */
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);
if (shortest) {
long now_sec, now_ms;
/* Calculate the time missing for the nearest
* timer to fire. */
// 得到当前系统时间
aeGetTime(&now_sec, &now_ms);
tvp = &tv;
tvp->tv_sec = shortest->when_sec - now_sec;
// 比较最近时间事件的时间和现在系统时间的差值
if (shortest->when_ms < now_ms) {
tvp->tv_usec = ((shortest->when_ms+1000) - now_ms)*1000;
tvp->tv_sec --;
} else {
tvp->tv_usec = (shortest->when_ms - now_ms)*1000;
}
// 差值小于零,说明这个时间事件已经到期,将这个时间事件的时间清零
if (tvp->tv_sec < 0) tvp->tv_sec = 0;
if (tvp->tv_usec < 0) tvp->tv_usec = 0;
} else {
/* If we have to check for events but need to return
* ASAP because of AE_DONT_WAIT we need to set the timeout
* to zero */
if (flags & AE_DONT_WAIT) {
// 设置文件事件不阻塞
tv.tv_sec = tv.tv_usec = 0;
tvp = &tv;
} else {
/* Otherwise we can block */
// 文件事件可以阻塞直到有事件到达为止
tvp = NULL; /* wait forever */
}
}
// 开始处理文件事件
numevents = aeApiPoll(eventLoop, tvp);
for (j = 0; j < numevents; j++) {
// 从fired数组中得到已经就绪的文件事件
aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];
int mask = eventLoop->fired[j].mask;
int fd = eventLoop->fired[j].fd;
int rfired = 0;
/* note the fe->mask & mask & ... code: maybe an already processed
* event removed an element that fired and we still didn't
* processed, so we check if the event is still valid. */
// 读事件处理
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++;
}
}
/* Check time events */
// 执行时间事件
if (flags & AE_TIME_EVENTS)
processed += processTimeEvents(eventLoop);
return processed; /* return the number of processed file/time events */
}
从这里看文件事件比时间事件优先处理
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 > 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 {
// 普通定时事件处理完成后则删除
aeDeleteTimeEvent(eventLoop, id);
}
te = eventLoop->timeEventHead;
} else {
te = te->next;
}
}
return processed;
}
redis中的事件处理
最新推荐文章于 2024-02-02 17:59:43 发布