Redis?Server啟動過程的詳細步驟
本文基于社區(qū)版Redis 4.0.8
1、 初始化參數(shù)配置
由函數(shù)initServerConfig()實現(xiàn),具體操作就是給配置參數(shù)賦初始化值:
//設置時區(qū) setlocale(LC_COLLATE,""); //設置隨機種子 char hashseed[16]; getRandomHexChars(hashseed,sizeof(hashseed)); dictSetHashFunctionSeed((uint8_t*)hashseed); //初始化module void initServerConfig(void) { ? ? //serverCron函數(shù)執(zhí)行頻率,默認10ms ? ? server.hz = CONFIG_DEFAULT_HZ; ? ? //監(jiān)聽端口,默認6379 ? ? server.port = CONFIG_DEFAULT_SERVER_PORT; ? ? server.tcp_backlog = CONFIG_DEFAULT_TCP_BACKLOG; ? ? server.dbnum = CONFIG_DEFAULT_DBNUM; ? ? ...... ? ? //初始化命令表 ? ? server.commands = dictCreate(&commandTableDictType,NULL); ? ? server.orig_commands = dictCreate(&commandTableDictType,NULL); ? ? populateCommandTable(); ? ? server.delCommand = lookupCommandByCString("del"); ? ? server.multiCommand = lookupCommandByCString("multi"); ? ? server.lpushCommand = lookupCommandByCString("lpush"); ? ? server.lpopCommand = lookupCommandByCString("lpop"); ? ? server.rpopCommand = lookupCommandByCString("rpop"); ? ? server.sremCommand = lookupCommandByCString("srem"); ? ? server.execCommand = lookupCommandByCString("exec"); ? ? server.expireCommand = lookupCommandByCString("expire"); ? ? server.pexpireCommand = lookupCommandByCString("pexpire"); ? ? ...... }
2、 加載并解析配置文件
在這一階段,會對命令行傳入的參數(shù)進行解析,并且調用 loadServerConfig 函數(shù),對命令行參數(shù)和配置文件中的參數(shù)進行合并處理,然后為 Redis 各功能模塊的關鍵參數(shù)設置合適的取值,以便 server 能高效地運行。
//filename表示配置文件全路徑名稱; //options表示命令行輸入的配置參數(shù),如port=4000 void loadServerConfig(char *filename, char *options) { ? ? sds config = sdsempty(); ? ? char buf[CONFIG_MAX_LINE+1]; ? ? /* 加載配置文件到內存 */ ? ? if (filename) { ? ? ? ? FILE *fp; ? ? ? ? if (filename[0] == '-' && filename[1] == '\0') { ? ? ? ? ? ? fp = stdin; ? ? ? ? } else { ? ? ? ? ? ? if ((fp = fopen(filename,"r")) == NULL) { ? ? ? ? ? ? ? ? serverLog(LL_WARNING, ? ? ? ? ? ? ? ? ? ? "Fatal error, can't open config file '%s'", filename); ? ? ? ? ? ? ? ? exit(1); ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? while(fgets(buf,CONFIG_MAX_LINE+1,fp) != NULL) ? ? ? ? ? ? config = sdscat(config,buf); ? ? ? ? if (fp != stdin) fclose(fp); ? ? } ? ? /* Append the additional options */ ? ? if (options) { ? ? ? ? config = sdscat(config,"\n"); ? ? ? ? config = sdscat(config,options); ? ? } ? ? //解析配置 ? ? loadServerConfigFromString(config); ? ? sdsfree(config); }
3、 初始化服務器內部變量
在完成對運行參數(shù)的解析和設置后,main 函數(shù)會調用 initServer 函數(shù),對 server 運行時的各種資源進行初始化工作。包括了 server 資源管理所需的數(shù)據(jù)結構初始化、鍵值對數(shù)據(jù)庫初始化、server 網絡框架初始化等。
最后調用 loadDataFromDisk 函數(shù),從磁盤上加載 AOF 或者是 RDB 文件,以便恢復之前的數(shù)據(jù)。
void initServer(void) { ? ? /* 初始化需要的各種資源 */ ? ? server.clients = listCreate();//初始化客戶端鏈表 ? ? server.pid = getpid(); ? ? server.current_client = NULL; ? ? server.clients = listCreate(); ? ? server.clients_to_close = listCreate(); ? ? server.slaves = listCreate(); ? ? server.monitors = listCreate(); ? ?server.clients_pending_write = listCreate(); ? ?server.slaveseldb = -1; /* Force to emit the first SELECT command. */ ? ?server.unblocked_clients = listCreate(); ? ?server.ready_keys = listCreate(); ? ?server.clients_waiting_acks = listCreate(); ? ?server.get_ack_from_slaves = 0; ? ?server.clients_paused = 0; ? ?server.system_memory_size = zmalloc_get_memory_size(); ? ?createSharedObjects(); ? ? //調用aeCreateEventLoop函數(shù)創(chuàng)建aeEventLoop結構體,并賦值給server結構的el變量 ? ? //maxclients 變量的值大小,可以在 Redis 的配置文件 redis.conf 中進行定義,默認值是 1000 ? ? server.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR); ? ? if (server.el == NULL) { ? ? ? ? serverLog(LL_WARNING, ? ? ? ? ? ? "Failed creating the event loop. Error message: '%s'", ? ? ? ? ? ? strerror(errno)); ? ? ? ? exit(1); ? ? } ? ? ...... ? ?? ? ? /* 創(chuàng)建數(shù)據(jù)庫結構*/ ? ? for (j = 0; j < server.dbnum; j++) { ? ? ? ? server.db[j].dict = dictCreate(&dbDictType,NULL); ? ? ? ? server.db[j].expires = dictCreate(&keyptrDictType,NULL); ? ? ? ? server.db[j].blocking_keys = dictCreate(&keylistDictType,NULL); ? ? ? ? server.db[j].ready_keys = dictCreate(&objectKeyPointerValueDictType,NULL); ? ? ? ? server.db[j].watched_keys = dictCreate(&keylistDictType,NULL); ? ? ? ? server.db[j].id = j; ? ? ? ? server.db[j].avg_ttl = 0; ? ? }? ? ?? ? ? ...... ? ? ? ?? ? ? //創(chuàng)建事件循環(huán)框架 ? ? server.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR); ? ? … ? ? //開始監(jiān)聽設置的網絡端口 ? ? if (server.port != 0 && ? ? ? ? ? ? listenToPort(server.port,server.ipfd,&server.ipfd_count) == C_ERR) ? ? ? ? ? ? exit(1); ? ? … ? ? //為server后臺任務創(chuàng)建定時事件 ? ? if (aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) { ? ? ? ? ? ? serverPanic("Can't create event loop timers."); ? ? ? ? ? ? exit(1); ? ? } ? ? … ? ? //為每一個監(jiān)聽的IP設置連接事件的處理函數(shù)acceptTcpHandler ? ? for (j = 0; j < server.ipfd_count; j++) { ? ? ? ? ? ? if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE, ? ? ? ? ? ? ? ? acceptTcpHandler,NULL) == AE_ERR) ? ? ? ? ? ?{ … } ? ? } }
4、執(zhí)行事件驅動框架
事件驅動框架是 Redis server 運行的核心。該框架一旦啟動后,就會一直循環(huán)執(zhí)行,每次循環(huán)會處理一批觸發(fā)的網絡讀寫事件。main 函數(shù)直接調用事件框架的主體函數(shù) aeMain(在ae.c文件中)后,就進入事件處理循環(huán)了。當然,在進入事件驅動循環(huán)前,main 函數(shù)會分別調用 aeSetBeforeSleepProc 和 aeSetAfterSleepProc 兩個函數(shù),來設置每次進入事件循環(huán)前 server 需要執(zhí)行的操作,以及每次事件循環(huán)結束后 server 需要執(zhí)行的操作。下面代碼顯示了這部分的執(zhí)行邏輯,你可以看下。
aeSetBeforeSleepProc(server.el,beforeSleep); aeSetAfterSleepProc(server.el,afterSleep); aeMain(server.el); aeDeleteEventLoop(server.el);
到此這篇關于Redis Server啟動過程的詳細步驟的文章就介紹到這了,更多相關Redis Server啟動過程 內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!