Redis源码剖析与实战
蒋德钧
中科院计算所副研究员
新⼈⾸单¥59.9
2334 人已学习
课程目录
已更新 13 讲 / 共 33 讲
0/4登录后,你可以任选4讲全文学习。
课前导读 (2讲)
开篇词 | 阅读Redis源码能给你带来什么?
免费
01 | 带你快速攻略Redis源码的整体架构
数据结构模块 (6讲)
02 | 键值对中字符串的实现,用char*还是结构体?
03 | 如何实现一个性能优异的Hash表?
04 | 内存友好的数据结构该如何细化设计?
05 | 有序集合为何能同时支持点查询和范围查询?
06 | 从ziplist到quicklist,再到listpack的启发
07 | 为什么Stream使用了Radix Tree?
事件驱动框架和执行模型模块 (5讲)
08 | Redis server启动后会做哪些操作?
09 | Redis事件驱动框架(上):何时使用select、poll、epoll?
10 | Redis事件驱动框架(中):Redis实现了Reactor模型吗?
11 | Redis事件驱动框架(下):Redis有哪些事件?
12 | Redis真的是单线程吗?
Redis源码剖析与实战
15
15
1.0x
00:00/00:00
登录|注册

08 | Redis server启动后会做哪些操作?

你好,我是蒋德钧。从这节课开始,我们就来到了课程的第二个模块,在这个模块里,我会带你了解和学习与 Redis 实例运行相关方面的知识,包括 Redis server 的启动过程、基于事件驱动框架的网络通信机制以及 Redis 线程执行模型。今天,我们先来学习下 Redis server 的启动过程。
我们知道,main 函数是 Redis 整个运行程序的入口,并且 Redis 实例在运行时,也会从这个 main 函数开始执行。同时,由于 Redis 是典型的 Client-Server 架构,一旦 Redis 实例开始运行,Redis server 也就会启动,而 main 函数其实也会负责 Redis server 的启动运行。
我在第 1 讲给你介绍过 Redis 源码的整体架构。其中,Redis 运行的基本控制逻辑是在server.c文件中完成的,而 main 函数就是在 server.c 中。
你平常在设计或实现一个网络服务器程序时,可能会遇到一个问题,那就是服务器启动时,应该做哪些操作、有没有一个典型的参考实现。所以今天这节课,我就从 main 函数开始,给你介绍下 Redis server 是如何在 main 函数中启动并完成初始化的。通过这节课内容的学习,你可以掌握 Redis 针对以下三个问题的实现思路:
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/1000字
划线
笔记
复制
该试读文章来自付费专栏《Redis源码剖析与实战》,如需阅读全部文章,
请订阅文章所属专栏新⼈⾸单¥59.9
立即订阅
登录 后留言

精选留言(6)

  • Kaito
    Redis 启动流程,主要的工作有:

    1、初始化前置操作(设置时区、随机种子)

    2、初始化 Server 的各种默认配置(server.c 的 initServerConfig 函数),默认配置见 server.h 中的 CONFIG_DEFAULT_XXX,比较典型的配置有:

    - 默认端口
    - 定时任务频率
    - 数据库数量
    - AOF 刷盘策略
    - 淘汰策略
    - 数据结构转换阈值
    - 主从复制参数

    3、加载配置启动参数,覆盖默认配置(config.c 的 loadServerConfig 函数):

    - 解析命令行参数
    - 解析配置文件

    3、初始化 Server(server.c 的 initServer 函数),例如会初始化:

    - 共享对象池
    - 客户端链表
    - 从库链表
    - 监听端口
    - 全局哈希表
    - LRU 池
    - 注册定时任务函数
    - 注册监听请求函数

    4、启动事件循环(ae.c 的 aeMain 函数)

    - 处理请求
    - 处理定时任务

    这里补充一下,初始化 Server 完成后,Redis 还会启动 3 类后台线程(server.c 的 InitServerLast 函数),协助主线程工作(异步释放 fd、AOF 每秒刷盘、lazyfree)。

    课后题:Redis 源码的 main 函数在调用 initServer 函数之前,会执行如下的代码片段,你知道这个代码片段的作用是什么吗?

    int main(int argc, char **argv) {
    ...
    server.supervised = redisIsSupervised(server.supervised_mode);
    int background = server.daemonize && !server.supervised;
    if (background) daemonize();
    ...
    }

    Redis 可以配置以守护进程的方式启动(配置文件 daemonize = yes),也可以把 Redis 托管给 upstart 或 systemd 来启动 / 停止(supervised = upstart|systemd|auto)。
    2021-08-12
    3
    7
  • 曾轼麟
    感谢老师的文章,先回答老师提出的问题:文章中代码片段的作用是?根据关键词我找到代码位于,main函数中loadServerConfig之后执行的,那么这块代码主要任务和顺序如下所示:
        1、此时redis的config是已经初始化完成的
        2、执行redisIsSupervised其目底主要是判断当redis进程是否运行中
        3、判断daemonize是否开启(如果启动设置了daemonize参数那么这里参数已经被填充)
        4、之前并没有存活的redis实例,并且开启了daemonize配置,那么执行daemonize函数挂起后台运行

    这里需要解释一下,执行daemonize()函数的时候本质是fork()进程,并不是大多文章说的那样是守护线程,父亲fork成功后是直接退出(在前端的效果就是,启动命令任务完成但是ps是有一个redis的进程),剩余的任务是交给fork出的子进程完成的(可以参考《深入理解操作系统》 第8章-异常控制流-进程中的内容)

    读完这篇文章后,我个人尝试画出整个redis启动的时序图,发现整个思路就很清晰了,建议同样阅读文章的同学可以尝试画一下,我总结一下我读完文章后的理解:
        1、redis的整体启动流程是按照 【初始化默认配置】->【解析启动命令】->【初始化server】->【初始化并启动事件驱动框架】 进行
        2、整个运行中的redis,其实就是一个永不停歇的while循环,位于aeMain中(运行中的事件驱动框架)
        3、在事件驱动框架中有两个钩子函数 beforeSleep 和 aftersleep,在每次while循环中都会触发这两个函数,后面用来实现事件触发的效果

    此外我发现了一个细节点:我在近期版本的redis6的分支上,发现在启动事件驱动框架之前(执行aeMain之前)会执行一个redisSetCpuAffinity函数,其效果有点类似于绑核的效果,那么是否可以认为从redis6开始其实不需要运维帮忙绑核redis自身就能做到绑核的效果呢?
    2021-08-13
    3
  • haha
    老师的这些图是用什么软件画的啊
    2021-08-21
  • 末日,成欢
    起始处设置随机种子是为了做什么?
    2021-08-12
    2
  • 那时刻
    请问老师,Redis server 会先读取 AOF;而如果没有 AOF,则再读取 RDB。为什么不先读rdb,再读aof呢?
    2021-08-12
    3
  • 可怜大灰狼
    先判断是否upstart或者systemd托管。再判断是否需要守护进程,内部还是fork+setsid
    2021-08-12
收起评论
6
返回
顶部