前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Redis 源码简洁剖析 07 - main 函数启动

Redis 源码简洁剖析 07 - main 函数启动

作者头像
Yano_nankai
发布2022-03-24 08:55:11
3200
发布2022-03-24 08:55:11
举报
文章被收录于专栏:二进制文集二进制文集

前言

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

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

问题

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

阶段 1:基本初始化

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

代码语言:javascript
复制
//设置时区
setlocale(LC_COLLATE,"");
tzset();
...
//设置随机种子
char hashseed[16];
getRandomHexChars(hashseed,sizeof(hashseed));
dictSetHashFunctionSeed((uint8_t*)hashseed);

image

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

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

代码语言:javascript
复制
// 判断 server 是否为「哨兵模式」
if (server.sentinel_mode) {
    // 初始化哨兵配置
    initSentinelConfig();
    // 初始化哨兵模式
    initSentinel();
}

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

代码语言:javascript
复制
// 运行的是 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 各功能模块的关键参数设置合适的取值。

代码语言:javascript
复制
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 文件,恢复之前的数据
代码语言:javascript
复制
// 初始化 server
initServer();
……

if (!server.sentinel_mode) {
    ……
    InitServerLast();
    // 从磁盘加载数据
    loadDataFromDisk();
    ……
} else {
    ……
    sentinelIsRunning();
    ……
}

资源管理

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

初始化数据库

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

代码语言:javascript
复制
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 函数来处理具体的连接。

代码语言:javascript
复制
//创建事件循环框架
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 函数进入事件驱动框架,循环处理各种触发的事件。

代码语言:javascript
复制
// 事件驱动框架,循环处理各种触发的事件
aeMain(server.el);
// 循环结束,删除 eventLoop
aeDeleteEventLoop(server.el);

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

代码语言:javascript
复制
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 下载链接,需要的小伙伴可以自取~

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022.02.09 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 问题
  • 阶段 1:基本初始化
  • 阶段 2:检查哨兵模式,执行 RDB 或 AOF 检测
  • 阶段 3:运行参数解析
  • 阶段 4:初始化 server
    • 资源管理
      • 初始化数据库
        • 创建事件驱动框架
        • 阶段 5:执行事件驱动框架
        • 参考链接
        • Redis 源码简洁剖析系列
        相关产品与服务
        云数据库 Redis
        腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档