在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},
.....

这个命令表相当多,省略了,基本是囊括了全部的可能命令。

毕竟服务端都是以上这些命令的响应实现嘛。以下是重点要学习的了,在服务端的运行主程序中。是怎样运行的呢。来一个流程框图:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQW5kcm9pZGx1c2hhbmdkZXJlbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

详细的代码实现为例如以下:

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操作相对简单非常多。

Redis源代码分析(三十五)--- redis.c服务端的实现分析(2)的更多相关文章

  1. JAVA之旅(三十四)——自定义服务端,URLConnection,正则表达式特点,匹配,切割,替换,获取,网页爬虫

    JAVA之旅(三十四)--自定义服务端,URLConnection,正则表达式特点,匹配,切割,替换,获取,网页爬虫 我们接着来说网络编程,TCP 一.自定义服务端 我们直接写一个服务端,让本机去连接 ...

  2. 从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十五║初探SSR服务端渲染(个人博客二)

    缘起 时间真快,现在已经是这个系列教程的下半部 Vue 第 12 篇了,昨天我也简单思考了下,可能明天再来一篇,Vue 就基本告一段落了,因为什么呢,这里给大家说个题外话,当时写博文的时候,只是想给大 ...

  3. ABP源码分析三十五:ABP中动态WebAPI原理解析

    动态WebAPI应该算是ABP中最Magic的功能之一了吧.开发人员无须定义继承自ApiController的类,只须重用Application Service中的类就可以对外提供WebAPI的功能, ...

  4. Vue(三十二)SSR服务端渲染Nuxt.js

    初始化Nuxt.js项目步骤 1.使用脚手架工具 create-nuxt-app 创建Nuxt项目 使用yarn或者npm $ yarn create nuxt-app <项目名> 注:根 ...

  5. JAVA之旅(三十五)——完结篇,终于把JAVA写完了,真感概呐!

    JAVA之旅(三十五)--完结篇,终于把JAVA写完了,真感概呐! 这篇博文只是用来水经验的,写这个系列是因为我自己的java本身也不是特别好,所以重温了一下,但是手比较痒于是就写出了这三十多篇博客了 ...

  6. Gradle 1.12用户指南翻译——第三十五章. Sonar 插件

    本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

  7. NeHe OpenGL教程 第三十五课:播放AVI

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  8. Python进阶(三十五)-Fiddler命令行和HTTP断点调试

    Python进阶(三十五)-Fiddler命令行和HTTP断点调试 一. Fiddler内置命令   上一节(使用Fiddler进行抓包分析)中,介绍到,在web session(与我们通常所说的se ...

  9. centos shell脚本编程1 正则 shell脚本结构 read命令 date命令的用法 shell中的逻辑判断 if 判断文件、目录属性 shell数组简单用法 $( ) 和${ } 和$(( )) 与 sh -n sh -x sh -v 第三十五节课

    centos   shell脚本编程1 正则  shell脚本结构  read命令  date命令的用法  shell中的逻辑判断  if 判断文件.目录属性  shell数组简单用法 $( ) 和$ ...

  10. Java进阶(三十五)java int与integer的区别

    Java进阶(三十五)java int与Integer的区别 前言 int与Integer的区别从大的方面来说就是基本数据类型与其包装类的区别: int 是基本类型,直接存数值,而Integer是对象 ...

随机推荐

  1. Git 使用规范流程【转】

    转自:http://www.ruanyifeng.com/blog/2015/08/git-use-process.html 作者: 阮一峰 日期: 2015年8月 5日 团队开发中,遵循一个合理.清 ...

  2. 006_mac osx 应用跨屏幕

    一般情况下 mac osx 中一个应用程序只能在一个屏幕上显示,作为从 windows 转过来的用户有点不太习惯,Goolge 后发现还是有解决方案的(虽然不是很好用). 打开 Mac 的系统偏好设置 ...

  3. idea项目左边栏只能看到文件看不到项目结构

    1.点击file->project structure..->Modules 点击右上角+加号 ->import Modules 2.选择你的项目,点击确定 3.在如下页面选择imp ...

  4. 【前端vue开发】vue开发总结

  5. android4.0后无法向Servlet发送请求解决办法

    从4.0开始,强制性地规定网络堵塞任务都不能放在ui线程,不然直接报错. 个办法,在oncreate下面加入 StrictMode.setThreadPolicy(new StrictMode.Thr ...

  6. JavaScript中的普通函数与构造函数

    问题 什么是构造函数? 构造函数与普通函数区别是什么? 用new关键字的时候到底做了什么? 构造函数有返回值怎么办? 构造函数能当普通函数调用吗? 以下是我的一些理解,理解错误的地方恳请大家帮忙指正, ...

  7. NET定时任务组件Hangfire

    开源的.NET定时任务组件Hangfire解析 项目慢慢就要开工了,很多园友都在问这个事情,看来大伙对这事很上心啊,事情需要一步步的来,尽量写出一个我们都满意的项目.以前每次在博客前面都会扯淡一下,不 ...

  8. Pomelo分布式游戏服务器框架

    Pomelo介绍&入门 目录 前言&介绍 安装Pomelo 创建项目并启动 创建项目 项目结构说明 启动 测试连接 聊天服务器 新建gate和chat服务器 配置master.json ...

  9. Argument 1 passed to Illuminate\Auth\SessionGuard::login() must be an instance of Illuminate\Contracts\Auth\Authenticatable, instance of App\User given,

    使用laravel内置的注册认证系统,注册账号,提示如下错误.Google之后,发现github的一个答案,解决了.分享一下 Argument 1 passed to Illuminate\Auth\ ...

  10. R语言编程艺术(4)R对数据、文件、字符串以及图形的处理

    本文对应<R语言编程艺术> 第8章:数学运算与模拟: 第10章:输入与输出: 第11章:字符串操作: 第12章:绘图 =================================== ...