redis是单进程单线程事件多路循环处理所有的客户端连接,它的运行都是靠事件触发的。
redis 的事件处理支持select、kqueue、epoll机制。其核心的poll函数aeApiPoll其实是一个封装函数,最终是调用 ae_select.c、ae_epoll.c还是ae_kqueue.c中的aeApiPoll(分别实现select、kqueue、epoll机制),取决于如下的宏定义:
#ifdef HAVE_EPOLL #include "ae_epoll.c" #else #ifdef HAVE_KQUEUE #include "ae_kqueue.c" #else #include "ae_select.c" #endif #endif
ae_select.c、ae_epoll.c、ae_kqueue.c分别对select、kqueue、epoll进制进行了封装,对select、kqueue、epoll的性能比较可在网上找到详细资料。
所有的事件保存在server.el中,el是如下的一个结构:
typedef struct aeEventLoop { int maxfd; long long timeEventNextId; aeFileEvent events[AE_SETSIZE]; /* Registered events */ aeFiredEvent fired[AE_SETSIZE]; /* Fired events */ aeTimeEvent *timeEventHead; int stop; void *apidata; /* This is used for polling API specific data */ aeBeforeSleepProc *beforesleep; } aeEventLoop;
其中maxfd是当前事件集合中最大的文件描述符id,timeEventNextId是下一个timer的id,events和fired分别保存了已注册的和已释放的文件event,timeEventHead指向一个timer event的链表,apidata保存了aeApiPoll的私有数据,其实也就是要监控的文件集合,具体实现要看采用哪种机制(select、kqueue、epoll三种机制)。stop用于停止事件循环,仅用于基准测试。beforesleep是在每次事件循环前都要被调用的函数,在main函数中被设置为beforeSleep函数。
对于文件event,其中mask为要检测的事件(读或者写),rfileProc、wfileProc分别为有读写事件时要调用的函数指针,clientData为函数要处理的数据;
typedef struct aeFileEvent { int mask; /* one of AE_(READABLE|WRITABLE) */ aeFileProc *rfileProc; aeFileProc *wfileProc; void *clientData; } aeFileEvent;
系统中的timer事件使用一个链表,每个timer有一个唯一的id,该timer在when_sec、 when_ms后被调用,调用函数为timeProc,timeProc处理的主要参数为clientData。在删除该timer时,需要调用 finalizerProc对clientData进行处理。
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;
Pingback 引用通告: redis事件处理机制及其它 - 数据库 - 开发者