Redis源码阅读(启动过程)

Redis的启动函数main()在redis.c文件中。

初始化配置

在启动时会使用initServerConfig()函数进行初始化服务器配置工作,redisServer(redis.h)作为保存服务器配置的结构。 redisServer包含以下的一些Redis服务器的信息:

  • 一般服务器状态。
  • 一些服务器统计信息。
  • 各种链表结构,如保存客户端的链表等。
  • 配置文件及启动参数中的配置。
  • 主从复制的状态。
  • 持久化文件的参数和状态。


初始化服务器数据结构

在initServer中会进行初始化服务器所需要的一些数据结构信息

  • 共享对象
  • 事件循环
  • 数据库结构
  • TCP套接字
  • 服务器cron
  • 事件循环
  • 打开AOF

恢复数据

如果开启了AOF持久化,那么加载AOF文件;否则加载RDB文件。

如果开启AOF,但是没有AOF文件的话也不会去读取RDB文件,这样会造成数据的丢失!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void loadDataFromDisk(void) {
// 记录开始时间
long long start = ustime();

// AOF 持久化已打开?
if (server.aof_state == REDIS_AOF_ON) {
// 尝试载入 AOF 文件
if (loadAppendOnlyFile(server.aof_filename) == REDIS_OK)
// 打印载入信息,并计算载入耗时长度
redisLog(REDIS_NOTICE,"DB loaded from append only file: %.3f seconds",(float)(ustime()-start)/1000000);
// AOF 持久化未打开
} else {
// 尝试载入 RDB 文件
if (rdbLoad(server.rdb_filename) == REDIS_OK) {
// 打印载入信息,并计算载入耗时长度
redisLog(REDIS_NOTICE,"DB loaded from disk: %.3f seconds",
(float)(ustime()-start)/1000000);
} else if (errno != ENOENT) {
redisLog(REDIS_WARNING,"Fatal error loading the DB: %s. Exiting.",strerror(errno));
exit(1);
}
}
}

运行事件处理器

Redis在每次进入事件循环时都会想调用函数beforeSleep()。
beforeSleep()会做两件事:

  1. 如果启用了虚拟内存系统,将会去刷新AOF到磁盘,由flushAppendOnlyFile()函数处理。
  2. 该函数封装了关于刷新缓冲区的一些复杂的逻辑,该缓冲区保存了AOF的写入缓存。

进入事件循环

Redis通过aeMain()进入主事件循环server.el。通过aeProcessEvents()处理所有已到达的时间事件和所有已就绪的文件事件。

处理请求并返回响应

经过上面的启动过程后,Redis已经处于主事件轮询循环中,监听端口并等待客户端连接。

处理新连接

在initServer()中,Redis注册acceptHandler(),当IO事件发生时被调用。acceptHandler()会创建一个客户端对象RedisClient。

从客户端读取命令

当客户端发送命令请求时,主事件循环调用readQueryFromClient()函数,它会尽可能多的读取(最多1024个字节)到临时缓冲区。
然后调用processInputBuffer()函数将客户端对象作为参数传递。processInputBuffer()将客户端的原始查询解析为执行Redis命令的参数,并解析每个参数的Redis字符串对象,并将它存储在客户端对象的数组中。然后调用processCommand()客户端对象来实际执行客户端发送的命令。
processCommand()从客户端获取命令的参数并执行。在执行前会进行许多的检查,如果检查失败,会向客户端对象附加一个错误消息并返回。

执行命令并响应

call()函数,从客户端对象中获取具体执行命令的指针所指向的函数对象,并调用。

1
2
3
4
5
6
7
void call(redisClient *c, struct redisCommand *cmd) {
long long dirty;

dirty = server.dirty;
cmd->proc(c);
dirty = server.dirty-dirty;
}