博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Redis源代码分析(三十五)--- redis.c服务端的实现分析(2)
阅读量:7237 次
发布时间:2019-06-29

本文共 8064 字,大约阅读时间需要 26 分钟。

       在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; }

方法非常easy命令,有人预计比較纳闷了,为什么没有连接操作呢,Client和Server不是要有连接操作的嘛,在这里为什么会没有呢。由于那些是client的主动进行的操作,所以服务端的main操作相对简单非常多。
你可能感兴趣的文章
c++知识点--extern "C" 读书笔记
查看>>
测试用例标准(一)
查看>>
八进制、十六进制表示
查看>>
海贼王:第86话 (希鲁鲁克的樱花与被继承的意志!)
查看>>
传输介质简介
查看>>
Symantec SEP 11.0简明安装管理
查看>>
linux命令行—《命令行快速入门》1
查看>>
设置resin在linux下的默认启动
查看>>
前端面试题
查看>>
Dell R720 安装VMware esx4.1u2
查看>>
SQLServer:镜像监控之oldest unsent transaction告警自愈
查看>>
php写xml文档的一种简单方式
查看>>
Spring使用注解装配之@Autowired
查看>>
oracle数据泵导入导出步骤
查看>>
Scaffold-DbContext
查看>>
职业生涯规划——掌握自己的命运
查看>>
ssh key
查看>>
【插件开发】—— 7 SWT布局详解,不能再详细了!
查看>>
火狐 Firefox https 访问时出现安全连接失败提示: 在服务器密钥交换握手信息中 SSL 收到了一个弱临时 Diffie-Hellman 密钥 解决办法...
查看>>
教训总结
查看>>