Redis 服务启动
直接看代码。新的版本中服务器启动代码在 server.c 中
int main(int argc, char **argv) {
struct timeval tv;
int j;
// 省略 ...
initServerConfig();
moduleInitModulesSystem();
if (argc >= 2) {
// ...
resetServerSaveParams();
loadServerConfig(configfile,options);// 根据指定的配置文件,初始化服务器配置
sdsfree(options);
}
initServer();// 初始化服务器数据结构,创建aeEventLoop,并注册端口监听
if (!server.sentinel_mode) {
// ...
moduleLoadFromQueue();
InitServerLast();
loadDataFromDisk();
// ...
} else {
InitServerLast();
sentinelIsRunning();
}
// ...
aeSetBeforeSleepProc(server.el,beforeSleep); // 钩子函数:每个event执行的前置函数
aeSetAfterSleepProc(server.el,afterSleep); // 钩子函数:在每个event执行完成后,执行
aeMain(server.el); // 事件循环处理
aeDeleteEventLoop(server.el); // 退出前清理事件
serverLog(LL_NOTICE, "Self add done");
return 0;
}
总的来说,服务器启动主要流程如下:
简单来说就是:
1.初始化服务器
2.初始化数据库、构建事件循环、监听端口
3.如果需要,从AOF/RDB中恢复数据
4.事件循环,等待客户端的请求
事件循环
Redis 处理的客户端请求、自己的时间周期函数,都是基于事件循环的。
首先在服务器启动时,就创建了 aeEventLoop ,函数如下。该在上边的 initServer 函数内部被调用
// 说明:0、1、2分别表示标准输入、标准输出、标准错误输出
aeEventLoop *aeCreateEventLoop(int setsize) {// 最大文件描述符,默认为 10000+128
aeEventLoop *eventLoop;
int i;
if ((eventLoop = zmalloc(sizeof(*eventLoop))) == NULL) goto err;
eventLoop->events = zmalloc(sizeof(aeFileEvent)*setsize);
eventLoop->fired = zmalloc(sizeof(aeFiredEvent)*setsize);
if (eventLoop->events == NULL || eventLoop->fired == NULL) goto err;
eventLoop->setsize = setsize;
eventLoop->lastTime = time(NULL);
eventLoop->timeEventHead = NULL;
eventLoop->timeEventNextId = 0;
eventLoop->stop = 0;
eventLoop->maxfd = -1;
eventLoop->beforesleep = NULL;
eventLoop->aftersleep = NULL;
if (aeApiCreate(eventLoop) == -1) goto err;
/* Events with mask == AE_NONE are not set. So let's initialize the
* vector with it. */
for (i = 0; i < setsize; i++) // fd就是这个数组的下标
eventLoop->events[i].mask = AE_NONE;// 表示暂时 未注册事件
return eventLoop;
err:
if (eventLoop) {
zfree(eventLoop->events);
zfree(eventLoop->fired);
zfree(eventLoop);
}
return NULL;
}
创建后的 aeEventLoop 结构在 redisServer 下,数据结构如下:
aeEventLoop 里面有两类事件:
aeFileEvent:文件事件,简单分为:
AE_READABLE :即可读事件
AE_WRITABLE :可写事件
AE_BARRIER :逆转事件处理顺序(对于同一个fd的事件可读可写时,注意还是一个事件,先处理读再处理写,避免clientData被覆盖丢失)
aeTimeEvent:时间事件
最后 aeFiredEvent 用于存储触发的待处理文件事件。
注意:上边 main 函数的最后,先设置钩子函数,再在 aeMain 中循环处理事件。接下来看下这个事件循环处理 aeMain 函数
void aeMain(aeEventLoop *eventLoop) {
eventLoop->stop = 0;
while (!eventLoop->stop) {
if (eventLoop->beforesleep != NULL)