请选择 进入手机版 | 继续访问电脑版

Redis中国用户组(CRUG)论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

搜索
热搜: 活动 交友 discuz
查看: 890|回复: 0

Redis源码分析(三十五)--- redis.c服务端的实现分析(2)

[复制链接]
  • TA的每日心情
    开心
    2017-8-30 15:46
  • 签到天数: 94 天

    [LV.6]常住居民II

    371

    主题

    481

    帖子

    3827

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    3827

    最佳新人活跃会员宣传达人突出贡献优秀版主荣誉管理论坛元老

    发表于 2016-4-19 09:41:54 | 显示全部楼层 |阅读模式

    在Redis服务端的代码量真的是比较大,如果一个一个API的学习怎么实现,无疑是一种效率很低的做法,所以我今天对服务端的实现代码的学习,重在他的执行流程上,而对于他的模块设计在上一篇中我已经分析过了,不明白的同学可以接着看上篇。所以我学习分析redis服务端的实现也是主要从main函数开始。在分析main执行流程之前,Redis的作者在这里声明了几个变量,这个我们有必要知道一下。

    1. /* Our shared "common" objects */  
    2. /* 共享的对象 */  
    3. struct sharedObjectsStruct shared;  
    4.   
    5. /* Global vars that are actually used as constants. The following double
    6. * values are used for double on-disk serialization, and are initialized
    7. * at runtime to avoid strange compiler optimizations. */  
    8. /* 全局的double类型常量 */  
    9. double R_Zero, R_PosInf, R_NegInf, R_Nan;  
    10.   
    11. /*================================= Globals ================================= */  
    12.   
    13. /* Global vars */  
    14. /* 全局的RedisServer */  
    15. struct redisServer server; /* server global state */  
    16.   
    17. /* Our command table.
    18. *
    19. * Every entry is composed of the following fields:
    20. *
    21. * name: a string representing the command name.
    22. * function: pointer to the C function implementing the command.
    23. * arity: number of arguments, it is possible to use -N to say >= N
    24. * sflags: command flags as string. See below for a table of flags.
    25. * flags: flags as bitmask. Computed by Redis using the 'sflags' field.
    26. * get_keys_proc: an optional function to get key arguments from a command.
    27. *                This is only used when the following three fields are not
    28. *                enough to specify what arguments are keys.
    29. * first_key_index: first argument that is a key
    30. * last_key_index: last argument that is a key
    31. * key_step: step to get all the keys from first to last argument. For instance
    32. *           in MSET the step is two since arguments are key,val,key,val,...
    33. * microseconds: microseconds of total execution time for this command.
    34. * calls: total number of calls of this command.
    35. *
    36. * The flags, microseconds and calls fields are computed by Redis and should
    37. * always be set to zero.
    38. *
    39. * Command flags are expressed using strings where every character represents
    40. * a flag. Later the populateCommandTable() function will take care of
    41. * populating the real 'flags' field using this characters.
    42. *
    43. * This is the meaning of the flags:
    44. *
    45. * w: write command (may modify the key space).
    46. * r: read command  (will never modify the key space).
    47. * m: may increase memory usage once called. Don't allow if out of memory.
    48. * a: admin command, like SAVE or SHUTDOWN.
    49. * p: Pub/Sub related command.
    50. * f: force replication of this command, regardless of server.dirty.
    51. * s: command not allowed in scripts.
    52. * R: random command. Command is not deterministic, that is, the same command
    53. *    with the same arguments, with the same key space, may have different
    54. *    results. For instance SPOP and RANDOMKEY are two random commands.
    55. * S: Sort command output array if called from script, so that the output
    56. *    is deterministic.
    57. * l: Allow command while loading the database.
    58. * t: Allow command while a slave has stale data but is not allowed to
    59. *    server this data. Normally no command is accepted in this condition
    60. *    but just a few.
    61. * M: Do not automatically propagate the command on MONITOR.
    62. * F: Fast command: O(1) or O(log(N)) command that should never delay
    63. *    its execution as long as the kernel scheduler is giving us time.
    64. *    Note that commands that may trigger a DEL as a side effect (like SET)
    65. *    are not fast commands.
    66. */  
    67. /* redis命令表格对应关系 */  
    68. struct redisCommand redisCommandTable[] = {  
    69.     {"get",getCommand,2,"rF",0,NULL,1,1,1,0,0},  
    70.     {"set",setCommand,-3,"wm",0,NULL,1,1,1,0,0},  
    71.     {"setnx",setnxCommand,3,"wmF",0,NULL,1,1,1,0,0},  
    72.     {"setex",setexCommand,4,"wm",0,NULL,1,1,1,0,0},  
    73. .....  
    复制代码

    这个命令表相当多,省略了,基本是囊括了所有的可能命令。毕竟服务端都是以上这些命令的响应实现嘛。下面是重点要学习的了,在服务端的执行主程序中,是如何执行的呢,来一个流程框图:


    具体的代码实现为如下:

    1. int main(int argc, char **argv) {  
    2.     struct timeval tv;  
    3.   
    4.     /* We need to initialize our libraries, and the server configuration. */  
    5. #ifdef INIT_SETPROCTITLE_REPLACEMENT  
    6.     spt_init(argc, argv);  
    7. #endif  
    8.     setlocale(LC_COLLATE,"");  
    9.     //启用线程安全模式  
    10.     zmalloc_enable_thread_safeness();  
    11.     //启用当发生内存溢出时的handler方法  
    12.     zmalloc_set_oom_handler(redisOutOfMemoryHandler);  
    13.     srand(time(NULL)^getpid());  
    14.     //获取当前时间  
    15.     gettimeofday(&tv,NULL);  
    16.     dictSetHashFunctionSeed(tv.tv_sec^tv.tv_usec^getpid());  
    17.     server.sentinel_mode = checkForSentinelMode(argc,argv);  
    18.     //初始化服务端的配置  
    19.     initServerConfig();  
    20.   
    21.     /* We need to init sentinel right now as parsing the configuration file
    22.      * in sentinel mode will have the effect of populating the sentinel
    23.      * data structures with master nodes to monitor. */  
    24.     //初始化服务端的模式  
    25.     if (server.sentinel_mode) {  
    26.         initSentinelConfig();  
    27.         initSentinel();  
    28.     }  
    29.   
    30.     if (argc >= 2) {  
    31.         int j = 1; /* First option to parse in argv[] */  
    32.         sds options = sdsempty();  
    33.         char *configfile = NULL;  
    34.   
    35.         /* Handle special options --help and --version */  
    36.         if (strcmp(argv[1], "-v") == 0 ||  
    37.             strcmp(argv[1], "--version") == 0) version();  
    38.         if (strcmp(argv[1], "--help") == 0 ||  
    39.             strcmp(argv[1], "-h") == 0) usage();  
    40.         if (strcmp(argv[1], "--test-memory") == 0) {  
    41.             if (argc == 3) {  
    42.                 memtest(atoi(argv[2]),50);  
    43.                 exit(0);  
    44.             } else {  
    45.                 fprintf(stderr,"Please specify the amount of memory to test in megabytes.\n");  
    46.                 fprintf(stderr,"Example: ./redis-server --test-memory 4096\n\n");  
    47.                 exit(1);  
    48.             }  
    49.         }  
    50.   
    51.         /* First argument is the config file name? */  
    52.         if (argv[j][0] != '-' || argv[j][1] != '-')  
    53.             configfile = argv[j++];  
    54.         /* All the other options are parsed and conceptually appended to the
    55.          * configuration file. For instance --port 6380 will generate the
    56.          * string "port 6380\n" to be parsed after the actual file name
    57.          * is parsed, if any. */  
    58.         while(j != argc) {  
    59.             if (argv[j][0] == '-' && argv[j][1] == '-') {  
    60.                 /* Option name */  
    61.                 if (sdslen(options)) options = sdscat(options,"\n");  
    62.                 options = sdscat(options,argv[j]+2);  
    63.                 options = sdscat(options," ");  
    64.             } else {  
    65.                 /* Option argument */  
    66.                 options = sdscatrepr(options,argv[j],strlen(argv[j]));  
    67.                 options = sdscat(options," ");  
    68.             }  
    69.             j++;  
    70.         }  
    71.         if (server.sentinel_mode && configfile && *configfile == '-') {  
    72.             redisLog(REDIS_WARNING,  
    73.                 "Sentinel config from STDIN not allowed.");  
    74.             redisLog(REDIS_WARNING,  
    75.                 "Sentinel needs config file on disk to save state.  Exiting...");  
    76.             exit(1);  
    77.         }  
    78.         if (configfile) server.configfile = getAbsolutePath(configfile);  
    79.         resetServerSaveParams();  
    80.         //加载服务端的配置,根据config配置文件来加载  
    81.         loadServerConfig(configfile,options);  
    82.         sdsfree(options);  
    83.     } else {  
    84.         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");  
    85.     }  
    86.     //是否开启守护进程  
    87.     if (server.daemonize) daemonize();  
    88.     initServer();  
    89.     if (server.daemonize) createPidFile();  
    90.     redisSetProcTitle(argv[0]);  
    91.     redisAsciiArt();  
    92.   
    93.     if (!server.sentinel_mode) {  
    94.         /* Things not needed when running in Sentinel mode. */  
    95.         redisLog(REDIS_WARNING,"Server started, Redis version " REDIS_VERSION);  
    96.     #ifdef __linux__  
    97.         linuxOvercommitMemoryWarning();  
    98.     #endif  
    99.         loadDataFromDisk();  
    100.         if (server.ipfd_count > 0)  
    101.             redisLog(REDIS_NOTICE,"The server is now ready to accept connections on port %d", server.port);  
    102.         if (server.sofd > 0)  
    103.             redisLog(REDIS_NOTICE,"The server is now ready to accept connections at %s", server.unixsocket);  
    104.     } else {  
    105.         sentinelIsRunning();  
    106.     }  
    107.   
    108.     /* Warning the user about suspicious maxmemory setting. */  
    109.     if (server.maxmemory > 0 && server.maxmemory < 1024*1024) {  
    110.         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);  
    111.     }  
    112.       
    113.     //事件加载之前调用的beforeSleep方法  
    114.     aeSetBeforeSleepProc(server.el,beforeSleep);  
    115.     //开启事件驱动循环  
    116.     aeMain(server.el);  
    117.     aeDeleteEventLoop(server.el);  
    118.     return 0;  
    119. }  
    复制代码

    方法非常简单命令,有人估计比较纳闷了,为什么没有连接操作呢,Client和Server不是要有连接操作的嘛,在这里为什么会没有呢,因为那些是客户端的主动进行的操作,所以服务端的main操作相对简单很多。


    转自:http://blog.csdn.net/androidlushangderen/article/details/40918041
    上一篇:Redis源码分析(三十四)--- redis.h服务端的实现分析(1)
    下一篇:Redis源码分析(三十六)--- Redis中的11大优秀设计


    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    阿里云
    阿里云

    Archiver|手机版|小黑屋|Redis中国用户组 ( 京ICP备15003959号

    GMT+8, 2017-9-21 20:26 , Processed in 0.117982 second(s), 33 queries .

    Powered by Discuz! X3.2

    © 2001-2013 Comsenz Inc.

    快速回复 返回顶部 返回列表