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
23void 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()会做两件事:
- 如果启用了虚拟内存系统,将会去刷新AOF到磁盘,由flushAppendOnlyFile()函数处理。
- 该函数封装了关于刷新缓冲区的一些复杂的逻辑,该缓冲区保存了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
7void call(redisClient *c, struct redisCommand *cmd) {
long long dirty;
dirty = server.dirty;
cmd->proc(c);
dirty = server.dirty-dirty;
}