Redis源码分析(二十四)——客户端与服务器
来源:互联网 发布:借钱软件哪个能借到钱 编辑:程序博客网 时间:2024/05/16 18:57
Redis的分析本该本该早点进行完的。由于各种安排的,一直没有对整个运行流程做一个分析总结。 今天特地补上这一点,也算有始有终。总的来说,通过对Redis的分析收获颇多, 同时也有很多地方确实做得不够好,尤其是后期,没能一鼓作气的完整分析下来,以致拖延到今天,说明在时间的计划安排上必须做得更好。
其中很多优美的设计可见Androidlushangderen的总结:Redis的十一大优秀设计。
客户端与服务器所设计的内容都是前面各小节所分析过的基本功能模块如:数据结构、数据类型、数据库、持久化、事务、事件处理等等的有机组合,使这些独立的模块组成我们的Redis系统。 因此,在这主要是分析服务器是如何开始、维持、关闭的整个流程。
服务器文件redis.h中定义了前面主要分析过的几个结构:数据对象类型redisObject、数据库redisDb、命令redisCommand、客户端redisClient、以及服务器本身redisServer 。 而这些独立体的是如何相互关联,组合的,可通过服务器主函数得知:
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(); zmalloc_set_oom_handler(redisOutOfMemoryHandler); srand(time(NULL)^getpid()); gettimeofday(&tv,NULL); dictSetHashFunctionSeed(tv.tv_sec^tv.tv_usec^getpid()); // 检查服务器是否以 Sentinel 模式启动 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. */ // 如果服务器以 Sentinel 模式启动,那么进行 Sentinel 功能相关的初始化 // 并为要监视的主服务器创建一些相应的数据结构 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 */ // 处理特殊选项 -h 、-v 和 --test-memory 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? */ // 如果第一个参数(argv[1])不是以 "--" 开头 // 那么它应该是一个配置文件 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. */ // 对用户给定的其余选项进行分析,并将分析所得的字符串追加稍后载入的配置文件的内容之后 // 比如 --port 6380 会被分析为 "port 6380\n" 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 (configfile) server.configfile = getAbsolutePath(configfile); // 重置保存条件 resetServerSaveParams(); // 载入配置文件, options 是前面分析出的给定选项 loadServerConfig(configfile,options); sdsfree(options); // 获取配置文件的绝对路径 if (configfile) server.configfile = getAbsolutePath(configfile); } 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(); // 如果服务器是守护进程,那么创建 PID 文件 if (server.daemonize) createPidFile(); // 为服务器进程设置名字 redisSetProcTitle(argv[0]); // 打印 ASCII LOGO redisAsciiArt(); // 如果服务器不是运行在 SENTINEL 模式,那么执行以下代码 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 // 从 AOF 文件或者 RDB 文件中载入数据 loadDataFromDisk(); // 启动集群? if (server.cluster_enabled) { if (verifyClusterConfigWithData() == REDIS_ERR) { redisLog(REDIS_WARNING, "You can't have keys in a DB different than DB 0 when in " "Cluster mode. Exiting."); exit(1); } } // 打印 TCP 端口 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. */ // 检查不正常的 maxmemory 配置 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); } // 运行事件处理器,一直到服务器关闭为止 aeSetBeforeSleepProc(server.el,beforeSleep); aeMain(server.el); // 服务器关闭,停止事件循环 aeDeleteEventLoop(server.el); return 0;}其主要流程如图:
一:初始化服务器
在启动服务器到能够接受客户端的网络连接之前,Redis需要执行一系列的初始化工作。主要包括:
1、初始化服务器的全局状态
2、载入配置文件
3、创建daemon进程,Redis默认以daemon进程的方式运行。
4、初始化服务器的各功能模块
5、载入数据
6、开始事件循环(开始接受客户端的连接请求,然后处理客户端命令,以及服务器自身的常规事务循环等操作)
初始化工作完成之后,服务器与个模块之间的关系如下:
二: 客户端连接到服务器
当服务器初始化完成之后,就能够接受客户端的套接字连接请求了。
三:服务器接受客户端命令请求、处理和返回命令处理结果
当客户端连接到服务器之后,就进入了服务器的常规事务循环状态,开始处理各客户端的命令请求并返回结果,同时服务器负责维持系统的正常运转,包括服务器本身的常规维护等工作。
- Redis源码分析(二十四)——客户端与服务器
- Redis源码分析(十四)——持久化RDB
- Redis源码分析(二十四)--- tool工具类(2)
- Netty源码分析(二)—客户端初始化
- Redis源码分析(二)——链表adlist
- Redis源码分析(二十)——事件ae
- Redis源码分析(二)——Redis数据结构-链表
- Redis源码分析(十四)--- rdb.c本地数据库操作
- 《Android源码设计模式解析与实战》读书笔记(二十四)——桥接模式
- Redis源码解析(二):redis之服务器-redis.c
- Redis 客户端源码分析+实现
- Redis Sentinel源码分析(二)
- Redis源码剖析和注释(二十四)--- Redis Sentinel实现(哨兵操作的深入剖析)
- netty源码分析(二十四)TCP粘包与拆包实例演示及分析
- Redis源码解析:14Redis服务器与客户端间的交互
- Redis源码分析(二十一)——慢查日志slow_log
- Redis源码分析(二十二)——CRC循环冗余校验
- Redis源码分析(二十三)——通用工具算法util
- 【leetcode 中序遍历】Binary Tree Inorder Traversal
- BlockingQueue
- Syslink Control使用技巧
- 利用基于控制器的加密方案进行数据保护(三)
- web开发笔记“javascript 刷新父级页面”
- Redis源码分析(二十四)——客户端与服务器
- redis数据类型
- 两个微软:从左右为难到左右逢源?
- 百度测试电面----一个纯白纸杯的测试用例
- jquery multiselect ajax动态获取数据
- Androio UI, ListView, ExpandableListView 可伸缩的ListView
- Map 类型 转换为JSON类型
- 如何判断 Java 线程并发的安全性
- 黄金数据查询演示示例