Redis implements its own event library. The event library is implemented in ae.c
.
redis实现了自己的事件库,这个事件库实现类在ae.c中
The best way to understand how the Redis event library works is to understand how Redis uses it.
理解redis事件库怎么工作的最好方式是理解redis怎么使用了它
Event Loop Initialization 初始化
initServer
function defined in redis.c
initializes the numerous fields of the redisServer
structure variable. One such field is the Redis event loop el
:
aeEventLoop *el
initServer
initializes server.el
field by calling aeCreateEventLoop
defined in ae.c
. The definition of aeEventLoop
is below:
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;
aeCreateEventLoop 创建一个定时事件的函数
aeCreateEventLoop
first malloc
s aeEventLoop
structure then calls ae_epoll.c:aeApiCreate
.
aeCreateEventLoop
首先分配 aeEventLoop
结构体,然后调用 ae_epoll.c:aeApiCreate
aeApiCreate
malloc
s aeApiState
that has two fields - epfd
that holds the epoll
file descriptor
aeApiCreate
分配 aeApiState ,它的epfd
字段绑定了一个 epoll
的文件描述符
returned by a call from epoll_create and events
that is of type struct epoll_event
define by the Linux epoll
library. The use of the events
field will be described later.
Next is ae.c:aeCreateTimeEvent
. But before that initServer
call anet.c:anetTcpServer
that creates and returns a listening descriptor. The descriptor listens on port 6379 by default. The returned listening descriptor is stored in server.fd
field.
redis的事件库是依赖linux的epoll
模型,自己封装的一套事件库
aeCreateTimeEvent 创建定时事件的参数
aeCreateTimeEvent
accepts the following as parameters:
eventLoop
: This isserver.el
inredis.c 执行定时事件的eventLoop
- milliseconds: The number of milliseconds from the current time after which the timer expires.
proc
: Function pointer. Stores the address of the function that has to be called after the timer expires. 超时时间到了后将要调用的函数地址clientData
: MostlyNULL
.finalizerProc
: Pointer to the function that has to be called before the timed event is removed from the list of timed events. 从时间轮时间中删除已执行的定时事件之前需要回调的函数地址
initServer
calls aeCreateTimeEvent
to add a timed event to timeEventHead
field of server.el
. timeEventHead
is a pointer to a list of such timed events. The call to aeCreateTimeEvent
from redis.c:initServer
function is given below:
initServer初始化服务
调用 aeCreateTimeEvent
创建一个定时事件赋值给server.el的timeEventHead字段
是一个指针,它执行一个定时事件列表,所有的定时事件都存到这里面。
timeEventHead
aeCreateTimeEvent(server.el /*eventLoop*/, 1 /*milliseconds*/, serverCron /*proc*/, NULL /*clientData*/, NULL /*finalizerProc*/);
redis.c:serverCron
performs many operations that helps keep Redis running properly.
aeCreateFileEvent 文件事件
The essence of aeCreateFileEvent
function is to execute epoll_ctl system call which adds a watch for EPOLLIN
event on the listening descriptor create by anetTcpServer
and associate it with the epoll
descriptor created by a call to aeCreateEventLoop
.
aeCreateFileEvent
本质上是通过 epoll_ctl 系统调用监听一个EPOLLIN
event 事件,这个EPOLLIN
event就是一个被anetTcpServer
创建的fd描述符,关联通过epoll
创建的aeCreateEventLoop描述符。
有点绕,用自己的话理解就是:aeCreateEventLoop创建的时候通过
epoll_creat创建了描述符fd0,然后aeCreateFileEvent将EPOLLIN
事件绑定的fd1,fd2.....描述符添加到
fd0中,用fd0来监听管理,模型可以参考:
Following is an explanation of what precisely aeCreateFileEvent
does when called from redis.c:initServer
.
initServer
passes the following arguments to aeCreateFileEvent
: 初始化参数
server.el
: The event loop created byaeCreateEventLoop
. Theepoll
descriptor is got fromserver.el
.server.fd
: The listening descriptor that also serves as an index to access the relevant file event structure from theeventLoop->events
table and store extra information like the callback function. 具体的事件描述符AE_READABLE
: Signifies thatserver.fd
has to be watched forEPOLLIN
event.acceptHandler
: The function that has to be executed when the event being watched for is ready. This function pointer is stored ineventLoop->events[server.fd]->rfileProc
.当监听的事件fd准备好了后执行的回调函数
This completes the initialization of Redis event loop.
这就完成了Redis事件循环的初始化。
- Event Loop本身初始化
- 初始化定时任务事件
aeCreateTimeEvent
- 初始化文件事件
aeCreateFileEvent
Event Loop Processing
ae.c:aeMain
called from redis.c:main
does the job of processing the event loop that is initialized in the previous phase.
ae.c:aeMain
calls ae.c:aeProcessEvents
in a while loop that processes pending time and file events.
一个主函数aeProcessEvents中开启一个while循环,在循环中处理事件事件和文件事件
aeProcessEvents
ae.c:aeProcessEvents
looks for the time event that will be pending in the smallest amount of time by calling ae.c:aeSearchNearestTimer
on the event loop. In our case there is only one timer event in the event loop that was created by ae.c:aeCreateTimeEvent
.
Remember, that the timer event created by aeCreateTimeEvent
has probably elapsed by now because it had an expiry time of one millisecond. Since the timer has already expired, the seconds and microseconds fields of the tvp
timeval
structure variable is initialized to zero.
创建定时事件的时候,如果和当前事件比较,创建好了久过期了,时间字段久直接设置成0
The tvp
structure variable along with the event loop variable is passed to ae_epoll.c:aeApiPoll
.
aeApiPoll
functions does an epoll_wait on the epoll
descriptor and populates the eventLoop->fired
table with the details:通过包装系统调用epoll_wait非阻塞获取准备好的fd列表
fd
: The descriptor that is now ready to do a read/write operation depending on the mask value.mask
: The read/write event that can now be performed on the corresponding descriptor.
aeApiPoll
returns the number of such file events ready for operation. Now to put things in context, if any client has requested for a connection then aeApiPoll
would have noticed it and populated the eventLoop->fired
table with an entry of the descriptor being the listening descriptor and mask being AE_READABLE
.
Now, aeProcessEvents
calls the redis.c:acceptHandler
registered as the callback. acceptHandler
executes accept on the listening descriptor returning a connected descriptor with the client. redis.c:createClient
adds a file event on the connected descriptor through a call to ae.c:aeCreateFileEvent
like below:
if (aeCreateFileEvent(server.el, c->fd, AE_READABLE,
readQueryFromClient, c) == AE_ERR) {
freeClient(c);
return NULL;
}
c
is the redisClient
structure variable and c->fd
is the connected descriptor.
Next the ae.c:aeProcessEvent
calls ae.c:processTimeEvents
processTimeEvents
ae.processTimeEvents
iterates over list of time events starting at eventLoop->timeEventHead
.
For every timed event that has elapsed processTimeEvents
calls the registered callback. In this case it calls the only timed event callback registered, that is, redis.c:serverCron
. The callback returns the time in milliseconds after which the callback must be called again. This change is recorded via a call to ae.c:aeAddMilliSeconds
and will be handled on the next iteration of ae.c:aeMain
while loop.
redis.c:serverCron 执行时间事件,每次执行返回下一次过多久再执行
That's all. 就这些吧!
- redis的事件库就是封装了linux的epoll系统调用,自己搞了一套事件模型
- 通过一个主eventLoop来执行文件事件和定时事件
- 文件事件就是fd的监听和回调处理
- 时间事件就是按照过期时间来创建的一个时间轮模型,A定时10s后执行,B定时20s后执行,C定时50s后执行,ABC按照过期时间的先后顺序组成一个时间环,主函数循环打捞执行