本系列终于更新到哨兵模块的介绍,由于哨兵模块涉及节点通信和选举等流程,所以笔者将其分为3个篇章进行剖析,而本文笔者将从源码分析的角度介绍一下redis哨兵是如何完成初始化的。
详解哨兵初始化流程
1. 哨兵基本数据结构
哨兵通过raft协议实现leader选举和故障转移线,针对这样一个场景,我们的哨兵一般会使用单数个,为了保证选举的正常进行哨兵还需要记录节一次每次进行选举的信息维护:
- 通过current_epoch记录当前选举的纪元。
- 用masters指针所指向的字典维护当前哨兵监听的master节点信息,每个master都会以sentinelRedisInstance结构体进行信息维护各自的name、slave等信息。
- 通过announce_ip和announce_port用于和其他哨兵联系时提供自身的地址信息。
对此我们给出sentinel 的结构体代码,读者可参考上述的介绍了解一下每一个核心字段:
2. 初始化哨兵基本配置
redis在启动会检查本次启动是否是通过redis-sentinel指令或者--sentinel参数启动哨兵,如果是则按照哨兵模式进行初始化,默认给该节点端口号为26379并初始化哨兵sentinel:
对应的我们给出核心代码段,可以看到main方法启动后会检查是否是通过redis-sentinel或者参数--sentinel启动,如果是则将sentinel_mode 设置为1,完成后续的配置和结构体初始化:
我们步入initSentinelConfig方法可以看到配置初始化只做了一件事,即将端口号设置为26379:
我们再查看initSentinel这个初始化哨兵结构体的函数,可以看到其内部会将当前server执行的命令表改为哨兵的命令,以及将所有IP、端口、masters指针进行初始化:
3. 初始化masters字典表
经历了上一步的初始化之后,redis就会开始解析redis.conf文件中解析出所有的master信息并存入masters中,假设我们在conf文件中键入如下配置:
redis就会从配置文件中匹配到sentinel 这个代码段,然后解析出<name> <host> <port> <quorum>这几个参数,生成一个master即可sentinelRedisInstance对象,存入masters这个字典中:
我们给出读取redis配置的核心代码段
我们再次步入sentinelHandleConfiguration可以看到大量配置参数解析的逻辑,流程比较简单就是字符串处理,我们就以本次的监听主节点的命令monitor为例,当redis解析到这个关键字则调用createSentinelRedisInstance解析出conf文件配置的master信息存入字典中:
最终我们步入createSentinelRedisInstance即可看到该方法通过与运算匹配出当前传入的信息是master的,于是拿到哨兵的masters字典表,完成master信息解析后将其存入字典中:
4. 启动并监听master
完成上述步骤后,redis得知当前节点是以哨兵模式启动,于是调用sentinelIsRunning方法,内部遍历masters节点的信息,发送到monitor频道告知其他当前哨兵监听的所有monitor信息
我们从入口看起,可以看到main方法后续会判断如果是哨兵模式则执行sentinelIsRunning:
其内部调用sentinelGenerateInitialMonitorEvents遍历masters表的信息将master发布到monitor频道上:
查看sentinelGenerateInitialMonitorEvents逻辑就是遍历masters表获取master信息调用sentinelEvent向主节点master的monitor频道上发布消息告知当前哨兵开始监控:
小结
我们简单小结一下redis哨兵的启动步骤:
- redis-server感知到启动模式为哨兵模式,则按照哨兵模式进行实例初始化。
- 加载哨兵模式支持的操作指令。
- 解析redis.conf配置中所有master信息存储到哨兵实例结构体的masters字典中。
- 遍历所有需要监控的master,向这些master的monitor频道发布monitor事件。
- 自此当前哨兵实例节点就开始监听主节点。