|
在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操作相对简单很多。
转自:http://blog.csdn.net/androidlushangderen/article/details/40918041
上一篇:Redis源码分析(三十四)--- redis.h服务端的实现分析(1)
下一篇:Redis源码分析(三十六)--- Redis中的11大优秀设计
|
|