前言

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. 【计组】《计算机组成与体系结构性能设计》William Stallings 第2部分 计算机系统 第3章 计算机功能和互连的顶层视图

    关键词 address bus 地址总线 asynchronous timing 异步时序 bus 总线 bus arbitration 总线仲裁 bus width 总线宽度 centralized ...

  2. uniapp蓝牙传输中文乱码问题

    问题描述:app接收到蓝牙传出过来的二进制数据,1.app进行arrbuff转成16进制字符串 // ArrayBuffer转16进度字符串示例 function ab2hex(buffer) { c ...

  3. Linux 离奇磁盘爆满解决办法

    问题原因&通用解决步骤 频繁收到es数据节点磁盘使用监控告警,到es上查看,磁盘使用率40%,因此登录该告警服务器,df -h 查看,如图 发下根目录使用超过了80%,因此持续告警,按照以往办 ...

  4. Java的generator工具类,数据库生成实体类和映射文件

    首先需要几个jar包: freemarker-2.3.23.jar log4j-1.2.16.jar mybatis-3.2.3.jar mybatis-generator-core-1.3.2.ja ...

  5. emqx的acl.conf使用

    allow_anonymous=true就不说了,打开这个就像开了挂 现在讨论一下allow_anonymous=false,这样的话你会发现,client连接不上了 后来发现连接和权限 是两个事.. ...

  6. Lombok的利弊

    1.介绍 Lombok是一个可以大量减少代码的工具, 通过Pluggable Annotation Processing API的方式解析注解, 在编译期为class文件注入getter,setter ...

  7. CSS基础 水平居中案例

    html结构 <body> <div class="father"> <div class="son"></div&g ...

  8. 《Python核心编程第3版中文版》(高清).PDF,免费无需任何解压密码

    链接:https://pan.baidu.com/s/18d3xinNX1oH5q8zpB10ABA 提取码:dx7h

  9. Linux中常用的监控性能的命令(sar、mpstat,vmstat, iostat,)详解

    Linux中常用的监控性能的命令有: sar:能查看CPU的平均信息,还能查看指定CPU的信息.与mpstat相比,sar能查看CPU历史信息 mpstat:能查看所有CPU的平均信息,还能查看指定C ...

  10. Linux系统使用crt登录之后如何显示横幅消息

    打开  /etc/motd 编辑内容即可 效果如下