前言

main 函数是 Redis 整个运行程序的入口。源码主要在 server.c 文件中。

前面 6 篇文章分析了 Redis 的基础数据结构。

问题

  • Redis server 启动后具体会做哪些初始化操作?
  • Redis server 初始化时有哪些关键配置项?
  • Redis server 如何开始处理客户端请求?

阶段 1:基本初始化

基本的初始化工作,包括设置 server 运行的时区等。

//设置时区
setlocale(LC_COLLATE,"");
tzset();
...
//设置随机种子
char hashseed[16];
getRandomHexChars(hashseed,sizeof(hashseed));
dictSetHashFunctionSeed((uint8_t*)hashseed);

阶段 2:检查哨兵模式,执行 RDB 或 AOF 检测

Redis Server 可能以哨兵模式运行。哨兵模式需要额外的参数配置及初始化。

// 判断 server 是否为「哨兵模式」
if (server.sentinel_mode) {
// 初始化哨兵配置
initSentinelConfig();
// 初始化哨兵模式
initSentinel();
}

此外还会检查是否要执行 RDB 检测或 AOF 检查,这对应了实际运行的程序是 redis-check-rdb 或 redis-check-aof。

// 运行的是 redis-check-rdb
if (strstr(argv[0],"redis-check-rdb") != NULL)
// 检测 RDB 文件
redis_check_rdb_main(argc,argv,NULL);
// 运行的是 redis-check-aof
else if (strstr(argv[0],"redis-check-aof") != NULL)
// 检测 AOF 文件
redis_check_aof_main(argc,argv);

阶段 3:运行参数解析

main 函数会对命令行传入的参数进行解析,并且调用 loadServerConfig 函数,对命令行参数和配置文件中的参数进行合并处理,然后为 Redis 各功能模块的关键参数设置合适的取值。

int main(int argc, char **argv) {

//保存命令行参数
for (j = 0; j < argc; j++) server.exec_argv[j] = zstrdup(argv[j]);

if (argc >= 2) {

//对每个运行时参数进行解析
while(j != argc) {

}

loadServerConfig(configfile,options);
}

loadServerConfig 函数是在 config.c 文件中实现的,该函数是以 Redis 配置文件和命令行参数的解析字符串为参数,将配置文件中的所有配置项读取出来,形成字符串。

阶段 4:初始化 server

调用 initServer 函数,对 server 运行时的各种资源进行初始化工作。这主要包括:

  • server 资源管理所需的数据结构初始化
  • 键值对数据库初始化
  • server 网络框架初始化

接着会再次判断是否为「哨兵模式」:

  • 是哨兵模式,调用 sentinelIsRunning 函数,设置启动哨兵模式
  • 不是哨兵模式,调用 loadDataFromDisk 函数,从磁盘加载 AOF 或 RDB 文件,恢复之前的数据
// 初始化 server
initServer();
…… if (!server.sentinel_mode) {
……
InitServerLast();
// 从磁盘加载数据
loadDataFromDisk();
……
} else {
……
sentinelIsRunning();
……
}

资源管理

和 server 连接的客户端、从库等,Redis 用作缓存时的替换候选集,以及 server 运行时的状态信息,这些资源的管理信息都会在 initServer 函数中进行初始化。

初始化数据库

因为一个 Redis 实例可以同时运行多个数据库,所以 initServer 函数会使用一个循环,依次为每个数据库创建相应的数据结构。

for (j = 0; j < server.dbnum; j++) {
// 创建全局哈希表
server.db[j].dict = dictCreate(&dbDictType,NULL);
// 创建过期 key 的信息表
server.db[j].expires = dictCreate(&dbExpiresDictType,NULL);
server.db[j].expires_cursor = 0;
// 为被 BLPOP 阻塞的 key 创建信息表
server.db[j].blocking_keys = dictCreate(&keylistDictType,NULL);
// 为将执行 PUSH 的阻塞 key 创建信息表
server.db[j].ready_keys = dictCreate(&objectKeyPointerValueDictType,NULL);
// 为被 MULTI/WATCH 操作监听的 key 创建信息表
server.db[j].watched_keys = dictCreate(&keylistDictType,NULL);
……
}

创建事件驱动框架

针对每个监听 IP 上可能发生的客户端连接,都创建了监听事件,用来监听客户端连接请求。同时,initServer 为监听事件设置了相应的处理函数 acceptTcpHandler。

这样一来,只要有客户端连接到 server 监听的 IP 和端口,事件驱动框架就会检测到有连接事件发生,然后调用 acceptTcpHandler 函数来处理具体的连接。

//创建事件循环框架
server.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR);

//开始监听设置的网络端口
if (server.port != 0 &&
listenToPort(server.port,server.ipfd,&server.ipfd_count) == C_ERR)
exit(1);

//为 server 后台任务创建定时事件
if (aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) {
serverPanic("Can't create event loop timers.");
exit(1);
}

阶段 5:执行事件驱动框架

高效处理高并发的客户端连接请求,Redis 采用了事件驱动框架,来并发处理不同客户端的连接和读写请求。main 函数最后会调用 aeMain 函数进入事件驱动框架,循环处理各种触发的事件。

// 事件驱动框架,循环处理各种触发的事件
aeMain(server.el);
// 循环结束,删除 eventLoop
aeDeleteEventLoop(server.el);

aeMain 函数核心调用了 aeProcessEvents 函数。aeProcessEvents 函数的具体源码将在之后的文章中分析。

void aeMain(aeEventLoop *eventLoop) {
eventLoop->stop = 0;
// 循环调用
while (!eventLoop->stop) {
// 核心函数,处理事件的逻辑
aeProcessEvents(eventLoop, AE_ALL_EVENTS|
AE_CALL_BEFORE_SLEEP|
AE_CALL_AFTER_SLEEP);
}
}

参考链接

Redis 源码简洁剖析系列

最简洁的 Redis 源码剖析系列文章

Java 编程思想-最全思维导图-GitHub 下载链接,需要的小伙伴可以自取~

原创不易,希望大家转载时请先联系我,并标注原文链接。

Redis 源码简洁剖析 07 - main 函数启动的更多相关文章

  1. Redis 源码简洁剖析 09 - Reactor 模型

    Reactor 模型 事件驱动框架 Redis 如何实现 Reactor 模型 事件的数据结构:aeFileEvent 主循环:aeMain 函数 事件捕获与分发:aeProcessEvents 函数 ...

  2. Redis 源码简洁剖析 02 - SDS 字符串

    C 语言的字符串函数 C 语言 string 函数,在 C 语言中可以使用 char* 字符数组实现字符串,C 语言标准库 string.h 中也定义了多种字符串操作函数. 字符串使用广泛,需要满足: ...

  3. Redis 源码简洁剖析 11 - 主 IO 线程及 Redis 6.0 多 IO 线程

    Redis 到底是不是单线程的程序? 多 IO 线程的初始化 IO 线程运行函数 IOThreadMain 如何推迟客户端「读」操作? 如何推迟客户端「写」操作? 如何把待「读」客户端分配给 IO 线 ...

  4. Redis 源码简洁剖析 03 - Dict Hash 基础

    Redis Hash 源码 Redis Hash 数据结构 Redis rehash 原理 为什么要 rehash? Redis dict 数据结构 Redis rehash 过程 什么时候触发 re ...

  5. Redis 源码简洁剖析 04 - Sorted Set 有序集合

    Sorted Set 是什么 Sorted Set 命令及实现方法 Sorted Set 数据结构 跳表(skiplist) 跳表节点的结构定义 跳表的定义 跳表节点查询 层数设置 跳表插入节点 zs ...

  6. Redis 源码简洁剖析 06 - quicklist 和 listpack

    quicklist 为什么要设计 quicklist 特点 数据结构 quicklistCreate quicklistDelIndex quicklistDelEntry quicklistInse ...

  7. Redis 源码简洁剖析 10 - aeEventLoop 及事件

    aeEventLoop IO 事件处理 IO 事件创建 读事件处理 写事件处理 时间事件处理 时间事件定义 时间事件创建 时间事件回调函数 时间事件的触发处理 参考链接 Redis 源码简洁剖析系列 ...

  8. Redis 源码简洁剖析 12 - 一条命令的处理过程

    命令的处理过程 Redis server 和一个客户端建立连接后,会在事件驱动框架中注册可读事件--客户端的命令请求.命令处理对应 4 个阶段: 命令读取:对应 readQueryFromClient ...

  9. Redis 源码简洁剖析 13 - RDB 文件

    RDB 是什么 RDB 文件格式 Header Body DB Selector AUX Fields Key-Value Footer 编码算法说明 Length 编码 String 编码 Scor ...

随机推荐

  1. 5G的到来

    通信改变未来,从古至今信息的传输和获取从来就没有缺少过,之所以谁能取得胜利就是谁掌握的资源多,其中信息资源尤为重要,只要获取到更多的信息你就能提前做出应对策略.因此未来一定是信息的未来,作为信息传输的 ...

  2. Cookie、Session、Token、JWT

    什么是认证(Authentication)------->就是验证当前用户的身份,证明"你是你自己" 互联网中的认证: 用户名密码登录 邮箱发送登录链接 手机号接收验证码 只 ...

  3. 【Java例题】5.4 排序集合的使用

    4.排序集合的使用.使用TreeSet模拟一个一维整数数组.其中,一维整数数组元素由Random类随机产生.最后显示排序后的结果. package chapter6; import java.util ...

  4. Bristol大学密码学博士生的五十二个知识点

    Bristol大学密码学博士生的五十二个知识点 这个系列,是Bristol大学的密码安全工作组为密码学和信息安全相关的博士准备了52个基本知识点. 原地址:http://bristolcrypto.b ...

  5. salesforce零基础学习(一百一十)list button实现的一些有趣事情

    本篇参考: salesforce零基础学习(九十五)lightning out https://developer.salesforce.com/docs/component-library/docu ...

  6. 第48篇-native方法调用解释执行的Java方法

    举一个native方法调用解释执行的Java方法的实例,如下: public class TestJNI { static { System.load("/media/mazhi/sourc ...

  7. Python pyecharts绘制词云图

    一.pyecharts绘制词云图WordCloud.add()方法简介 WordCloud.add()方法简介 add(name,attr,value, shape="circle" ...

  8. Linux搜索查找类

    find find将从指定目录下递归地遍历各个子目录,将满足条件的文件或目录显示在终端,如果没有则不显示 基本语法 find 搜索范围 [选项] 选项说明 选项 功能 -name 按照文件名查找 -u ...

  9. HTML5 +Java基础 大一结业认证考试试题 - 云南农业职业技术学院 - 互联网技术学院 - 美和易思校企合作专业

     第1题 [单选题][0.33分][概念理解] 关于java中的逻辑运算符,下列说法正确的是 逻辑运算符||.&&.!都是用于连接两个关系表达式</p> 当&&am ...

  10. Java高效开发-fiddler抓包工具

    1.简介 Fiddler是最常用的抓包工具之一,只要打开之后就能够实现数据包抓取,关闭之后会自动取消代理,非常方便本地调试 2.下载 阿里云盘地址:https://www.aliyundrive.co ...