我們知道,Redis是一個性能非常優異的kv服務器,有關redis的性能及適用場景,在后期做介紹,這里重點介紹下redis的啟動流程,也是對近期對redis代碼閱讀的一點總結,有不足之處,歡迎拍磚.
閱讀c/c++項目的源碼,一般情況下,都將從main函數,那么對於redis的啟動流程,下邊也從main函數開始說起.
首先,main函數里邊聲明了一個time_t start 變量, 用來對一些操作進行時間統計,如從AOF文件中加載數據,從redisdb中加載數據。
接下來,調用initServerConfig() 對struct redisServer server 這一個全部變量進行默認初始化.(如果啟動redis時指定了redis.conf,后邊會用配置文件中的配置覆蓋這里初始化的值)。 然后是對命令行參數的讀取,參數個數不能超過3個, 可以以test-memory指定內存數運行redis;
如果啟動參數指定了redis.conf, 那么首先調用resetServerSaveParams() 重置server.saveparams(釋放該指針指向的內存單元並設置為NULL, 同時將server.saveparamslen置為0):
void resetServerSaveParams() { zfree(server.saveparams); server.saveparams = NULL; server.saveparamslen = 0; }
然后調用loadServerConfig(char *filename) 對 server 全局變量重新初始化。(具體實現方式暫時不做具體分析)
判斷 server.daemonize,已決定是否一daemon方式啟動redis.
if (server.daemonize) daemonize();
接下來調用 initServer(), 初始化服務器,初略來說,這個函數完成的工作有:
調用 createSharedObjects() 初始化 全局的shared對象
調用 aeCreateEventLoop() 創建事件輪詢:
server.el = aeCreateEventLoop(); aeCreateEventLoop完成的工作: 1:為aeEventLoop分配內存: eventLoop = zmalloc(sizeof(*eventLoop)); 2:調用aeApiCreate(aeEventLoop) 對eventLoop進行初始化,在這個函數里邊, 首先調用zmalloc為aeApiState 分配內存, 然后調用epoll_create創建一個fd, 並將其賦值給 aeApiState的epfd, 然后整個aeApiState 賦值給eventLoop的apidata.
typedef struct aeApiState { int epfd; struct epoll_event events[AE_SETSIZE]; } aeApiState; static int aeApiCreate(aeEventLoop *eventLoop) { aeApiState *state = zmalloc(sizeof(aeApiState)); if (!state) return -1; state->epfd = epoll_create(1024); /* 1024 is just an hint for the kernel */ if (state->epfd == -1) return -1; eventLoop->apidata = state; return 0; } aeEventLoop *aeCreateEventLoop(void) { aeEventLoop *eventLoop; int i; eventLoop = zmalloc(sizeof(*eventLoop)); if (!eventLoop) return NULL; eventLoop->timeEventHead = NULL; eventLoop->timeEventNextId = 0; eventLoop->stop = 0; eventLoop->maxfd = -1; eventLoop->beforesleep = NULL; if (aeApiCreate(eventLoop) == -1) { zfree(eventLoop); return NULL; } /* Events with mask == AE_NONE are not set. So let's initialize the * vector with it. */ for (i = 0; i < AE_SETSIZE; i++) eventLoop->events[i].mask = AE_NONE; return eventLoop; }
然后調用anetTcpServer和anetUnixServer 創建對端口和unix域套接字的監聽, 並將返回值賦值給 server.ipfd和server.sofd。通知給這兩個套接字設置AE_READABLE事件,並設置相應的處理函數
if (server.port != 0) { server.ipfd = anetTcpServer(server.neterr,server.port,server.bindaddr); if (server.ipfd == ANET_ERR) { redisLog(REDIS_WARNING, "Opening port %d: %s", server.port, server.neterr); exit(1); } } if (server.unixsocket != NULL) { unlink(server.unixsocket); /* don't care if this fails */ server.sofd = anetUnixServer(server.neterr,server.unixsocket,server.unixsocketperm); if (server.sofd == ANET_ERR) { redisLog(REDIS_WARNING, "Opening socket: %s", server.neterr); exit(1); } } if (server.ipfd > 0 && aeCreateFileEvent(server.el,server.ipfd,AE_READABLE, acceptTcpHandler,NULL) == AE_ERR) oom("creating file event"); if (server.sofd > 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE, acceptUnixHandler,NULL) == AE_ERR) oom("creating file event");
判斷是否需要打開aof文件。
if (server.appendonly) { server.appendfd = open(server.appendfilename,O_WRONLY|O_APPEND|O_CREAT,0644); if (server.appendfd == -1) { redisLog(REDIS_WARNING, "Can't open the append-only file: %s", strerror(errno)); exit(1); } }
initServer 函數返回后, 根據server.appendonly的值判斷是否需要從aof文件或者rdb裝載數據。
start = time(NULL); if (server.appendonly) { if (loadAppendOnlyFile(server.appendfilename) == REDIS_OK) redisLog(REDIS_NOTICE,"DB loaded from append only file: %ld seconds",time(NULL)-start); } else { if (rdbLoad(server.dbfilename) == REDIS_OK) { redisLog(REDIS_NOTICE,"DB loaded from disk: %ld seconds", time(NULL)-start); } else if (errno != ENOENT) { redisLog(REDIS_WARNING,"Fatal error loading the DB. Exiting."); exit(1); } }
然后就設置每次進入事件處理函數之前需要執行的函數
aeSetBeforeSleepProc(server.el,beforeSleep);
然后調用aeMain(server.el);開始事件循環。