標籤:nosql資料庫 源碼 記憶體資料庫
在Redis服務端的代碼量真的是比較大,如果一個一個API的學習怎麼實現,無疑是一種效率很低的做法,所以我今天對服務端的實現代碼的學習,重在他的執行流程上,而對於他的模組設計在上一篇中我已經分析過了,不明白的同學可以接著看上篇。所以我學習分析redis服務端的實現也是主要從main函數開始。在分析main執行流程之前,Redis的作者在這裡聲明了幾個變數,這個我們有必要知道一下。
/* Our shared "common" objects *//* 共用的對象 */struct sharedObjectsStruct shared;/* Global vars that are actually used as constants. The following double * values are used for double on-disk serialization, and are initialized * at runtime to avoid strange compiler optimizations. *//* 全域的double類型常量 */double R_Zero, R_PosInf, R_NegInf, R_Nan;/*================================= Globals ================================= *//* Global vars *//* 全域的RedisServer */struct redisServer server; /* server global state *//* Our command table. * * Every entry is composed of the following fields: * * name: a string representing the command name. * function: pointer to the C function implementing the command. * arity: number of arguments, it is possible to use -N to say >= N * sflags: command flags as string. See below for a table of flags. * flags: flags as bitmask. Computed by Redis using the 'sflags' field. * get_keys_proc: an optional function to get key arguments from a command. * This is only used when the following three fields are not * enough to specify what arguments are keys. * first_key_index: first argument that is a key * last_key_index: last argument that is a key * key_step: step to get all the keys from first to last argument. For instance * in MSET the step is two since arguments are key,val,key,val,... * microseconds: microseconds of total execution time for this command. * calls: total number of calls of this command. * * The flags, microseconds and calls fields are computed by Redis and should * always be set to zero. * * Command flags are expressed using strings where every character represents * a flag. Later the populateCommandTable() function will take care of * populating the real 'flags' field using this characters. * * This is the meaning of the flags: * * w: write command (may modify the key space). * r: read command (will never modify the key space). * m: may increase memory usage once called. Don't allow if out of memory. * a: admin command, like SAVE or SHUTDOWN. * p: Pub/Sub related command. * f: force replication of this command, regardless of server.dirty. * s: command not allowed in scripts. * R: random command. Command is not deterministic, that is, the same command * with the same arguments, with the same key space, may have different * results. For instance SPOP and RANDOMKEY are two random commands. * S: Sort command output array if called from script, so that the output * is deterministic. * l: Allow command while loading the database. * t: Allow command while a slave has stale data but is not allowed to * server this data. Normally no command is accepted in this condition * but just a few. * M: Do not automatically propagate the command on MONITOR. * F: Fast command: O(1) or O(log(N)) command that should never delay * its execution as long as the kernel scheduler is giving us time. * Note that commands that may trigger a DEL as a side effect (like SET) * are not fast commands. *//* redis命令表格對應關係 */struct redisCommand redisCommandTable[] = { {"get",getCommand,2,"rF",0,NULL,1,1,1,0,0}, {"set",setCommand,-3,"wm",0,NULL,1,1,1,0,0}, {"setnx",setnxCommand,3,"wmF",0,NULL,1,1,1,0,0}, {"setex",setexCommand,4,"wm",0,NULL,1,1,1,0,0},.....
這個命令表相當多,省略了,基本是囊括了所有的可能命令。畢竟服務端都是以上這些命令的響應實現嘛。下面是重點要學習的了,在服務端的執行主程式中,是如何執行的呢,來一個流程框圖:
具體的代碼實現為如下:
int main(int argc, char **argv) { struct timeval tv; /* We need to initialize our libraries, and the server configuration. */#ifdef INIT_SETPROCTITLE_REPLACEMENT spt_init(argc, argv);#endif setlocale(LC_COLLATE,"");//啟用安全執行緒模式 zmalloc_enable_thread_safeness(); //啟用當發生記憶體溢出時的handler方法 zmalloc_set_oom_handler(redisOutOfMemoryHandler); srand(time(NULL)^getpid()); //擷取目前時間 gettimeofday(&tv,NULL); dictSetHashFunctionSeed(tv.tv_sec^tv.tv_usec^getpid()); server.sentinel_mode = checkForSentinelMode(argc,argv); //初始化服務端的配置 initServerConfig(); /* We need to init sentinel right now as parsing the configuration file * in sentinel mode will have the effect of populating the sentinel * data structures with master nodes to monitor. */ //初始化服務端的模式 if (server.sentinel_mode) { initSentinelConfig(); initSentinel(); } if (argc >= 2) { int j = 1; /* First option to parse in argv[] */ sds options = sdsempty(); char *configfile = NULL; /* Handle special options --help and --version */ if (strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "--version") == 0) version(); if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) usage(); if (strcmp(argv[1], "--test-memory") == 0) { if (argc == 3) { memtest(atoi(argv[2]),50); exit(0); } else { fprintf(stderr,"Please specify the amount of memory to test in megabytes.\n"); fprintf(stderr,"Example: ./redis-server --test-memory 4096\n\n"); exit(1); } } /* First argument is the config file name? */ if (argv[j][0] != '-' || argv[j][1] != '-') configfile = argv[j++]; /* All the other options are parsed and conceptually appended to the * configuration file. For instance --port 6380 will generate the * string "port 6380\n" to be parsed after the actual file name * is parsed, if any. */ while(j != argc) { if (argv[j][0] == '-' && argv[j][1] == '-') { /* Option name */ if (sdslen(options)) options = sdscat(options,"\n"); options = sdscat(options,argv[j]+2); options = sdscat(options," "); } else { /* Option argument */ options = sdscatrepr(options,argv[j],strlen(argv[j])); options = sdscat(options," "); } j++; } if (server.sentinel_mode && configfile && *configfile == '-') { redisLog(REDIS_WARNING, "Sentinel config from STDIN not allowed."); redisLog(REDIS_WARNING, "Sentinel needs config file on disk to save state. Exiting..."); exit(1); } if (configfile) server.configfile = getAbsolutePath(configfile); resetServerSaveParams(); //載入服務端的配置,根據config設定檔來載入 loadServerConfig(configfile,options); sdsfree(options); } else { redisLog(REDIS_WARNING, "Warning: no config file specified, using the default config. In order to specify a config file use %s /path/to/%s.conf", argv[0], server.sentinel_mode ? "sentinel" : "redis"); } //是否開啟守護進程 if (server.daemonize) daemonize(); initServer(); if (server.daemonize) createPidFile(); redisSetProcTitle(argv[0]); redisAsciiArt(); if (!server.sentinel_mode) { /* Things not needed when running in Sentinel mode. */ redisLog(REDIS_WARNING,"Server started, Redis version " REDIS_VERSION); #ifdef __linux__ linuxOvercommitMemoryWarning(); #endif loadDataFromDisk(); if (server.ipfd_count > 0) redisLog(REDIS_NOTICE,"The server is now ready to accept connections on port %d", server.port); if (server.sofd > 0) redisLog(REDIS_NOTICE,"The server is now ready to accept connections at %s", server.unixsocket); } else { sentinelIsRunning(); } /* Warning the user about suspicious maxmemory setting. */ if (server.maxmemory > 0 && server.maxmemory < 1024*1024) { redisLog(REDIS_WARNING,"WARNING: You specified a maxmemory value that is less than 1MB (current value is %llu bytes). Are you sure this is what you really want?", server.maxmemory); }//事件載入之前調用的beforeSleep方法 aeSetBeforeSleepProc(server.el,beforeSleep); //開啟事件驅動迴圈 aeMain(server.el); aeDeleteEventLoop(server.el); return 0;}
方法非常簡單命令,有人估計比較納悶了,為什麼沒有串連操作呢,Client和Server不是要有串連操作的嘛,在這裡為什麼會沒有呢,因為那些是用戶端的主動進行的操作,所以服務端的main操作相對簡單很多。
Redis源碼分析(三十五)--- redis.c服務端的實現分析(2)