写点什么

Redis Sentinel 源码:Redis 的高可用模型分析

发布于: 2021 年 02 月 08 日

摘要:本文通过对 Redis Sentinel 源码的理解,详细说明 Sentinel 的代码实现方式。


Redis Sentinel 是 Redis 提供的高可用模型解决方案。Sentinel 可以自动监测一个或多个 Redis 主备实例,并在主实例宕机的情况下自动实行主备倒换。本文通过对 Redis Sentinel 源码的理解,详细说明 Sentinel 的代码实现方式。


Sentinel 使用 Redis 内核相同的事件驱动代码框架, 但 Sentinel 有自己独特的初始化步骤。在这篇文章里,会从 Sentinel 的初始化、Sentinel 主时间事件函数、Sentinel 网络连接和 Tilt 模式三部分进行讲解。


Sentinel 初始化


我们可以通过 redis-sentinel <path-to-configfile> 或者 redis-server <path-to-configfile> --sentinel 这两种方式启动并运行 Sentinel 实例,这两种方式是等价的。在 Redis server.c 的 main 函数中,我们会看到 Redis 如何判断用户指定以 Sentinel 方式运行的逻辑:



其中 checkForSentinelMode 函数会监测以下两种条件:


  1. 程序使用 redis-sentinel 可执行文件执行。

  2. 程序参数列表中有--sentinel 标志。


以上任何一种条件成立则 Redis 会使用 Sentinel 的方式运行。



在 Redis 判断是否以 Sentinel 的方式运行以后,我们会看到如下代码段:



在 initSentinelConfig 函数中,会使用 Sentinel 特定的端口(默认为 26379)来替代 Redis 的默认端口(6379)。另外,在 Sentinel 模式下,需要禁用服务器运行保护模式。



与此同时,initSentinel 函数会做如下操作:



1.使用 Sentinel 自带的命令表去替代 Redis 服务器原生的命令. Sentinel 支持的命令表如下:



2.初始化 Sentinel 主状态结构,Sentinel 主状态的定义及注释如下。




其中 masters 字典指针中的每个值都对应着一个 Sentinel 检测的主实例。


在读取配置信息后,Redis 服务器主函数会调用 sentinelIsRunning 函数, 做以下几个工作:


  1. 检查配置文件是否被设置,并且检查程序对配置文件是否有写权限,因为如果 Sentinel 状态改变的话,会不断将自己当前状态记录在配置文件中。

  2. 如果在配置文件中指定运行 ID,Sentinel 会使用这个 ID 作为运行 ID,相反地,如果没有指定运行 ID,Sentinel 会生成一个 ID 用来作为 Sentinel 的运行 ID。

  3. 对所有的 Sentinel 监测实例产生初始监测事件。



Sentinel 的主时间事件函数


Sentinel 使用和 Redis 服务器相同的事件处理机制:分为文件事件和时间事件。文件事件处理机制使用 I/O 多路复用来处理服务器端的网络 I/O 请求,例如客户端连接,读写等操作。时间处理机制则在主循环中周期性调用时间函数来处理定时操作,例如服务器端的维护,定时更新,删除等操作。Redis 服务器主时间函数是在 server.c 中定义的 serverCron 函数,在默认情况下,serverCron 会每 100ms 被调用一次。在这个函数中,我们看到如下代码:



其中当服务器以 sentinel 模式运行的时候,serverCron 会调用 sentinelTimer 函数,来运行 Sentinel 中的主逻辑,sentinelTimer 函数在 sentinel.c 中的定义如下:



Sentinel Timer 函数会做如下几个操作:


  1. 检查 Sentinel 当前是否在 Tilt 模式(Tilt 模式将会在稍后章节介绍)。

  2. 检查 Sentinel 与其监控主备实例,以及其他 Sentinel 实例的连接,更新当前状态,并在主实例下线的时候自动做主备倒换操作。

  3. 检查回调脚本状态,并做相应操作。

  4. 更新服务器频率(调用 serverCron 函数的频率),加上一个随机因子,作用是防止监控相同主节点的 Sentinel 在选举 Leader 的时候时间冲突,导致选举无法产生绝对多的票数。


其中 SentinelHandleDictOfRedisInstances 函数的定义如下: 



SentinelHandleDictOfRedisInstances 函数主要做的工作是:


1.调用 sentinelHandleDictOfRedisInstance 函数处理 Sentinel 与其它特定实例连接,状态更 新,以及主备倒换工作。


  1. 如果当前处理实例为主实例,递归调用 SentinelHandleDictOfRedisInstances 函数处理其下属的从实例以及其他监控这个主实例的 Sentinel。

  2. 在主备倒换成功的情况下,更新主实例为升级为主实例的从实例。


其中在 sentinelHandleRedisInstance 的定义如下:



这个函数会做以下两部分操作:


1.检查 Sentinel 和其他实例(主备实例以及其他 Sentinel)的连接,如果连接没有设置或已经断开连接,Sentinel 会重试相对应的连接,并定时发送响应命令。 需要注意的是:Sentinel 和每个主备实例都有两个连接,命令连接和发布订阅连接。但是与其他监听相同主备实例的 Sentinel 只保留命令连接,这部分细节会在网络章节单独介绍。


2.第二部分操作主要做的是监测主备及其他 Sentinel 实例,并监测其是否在主观下线状态,对于主实例来说,还要检测是否在客观下线状态,并进行相应的主备倒换操作。


需要注意的是第二部分操作如果 Sentinel 在 Tilt 模式下是忽略的,下面我们来看一下这个函数第二部分的的具体实现细节。


sentinelCheckSubjectivelyDown 函数会监测特定的 Redis 实例(主备实例以及其他 Sentinel)是否处于主观下线状态,这部分函数代码如下:



主观下线状态意味着特定的 Redis 实例满足以下条件之一:


  1. 在实例配置的 down_after_milliseconds 时间内没有收到 Ping 的回复。

  2. Sentinel 认为实例是主实例,但收到实例为从实例的回复,并且上次实例角色回复时间大于在实例配置的 down_after_millisecon 时间加上 2 倍 INFO 命令间隔。


如果任何一个条件满足,Sentinel 会打开实例的 S_DOWN 标志并认为实例进入主观下线状态。


主观下线状态意味着 Sentinel 主观认为实例下线,但此时 Sentinel 并没有询问其他监控此实例的其他 Sentinel 此实例的在线状态。


sentinelCheckObjectivelyDown 函数会检查实例是否为客观下线状态,这个操作仅仅对主实例进行。sentinelCheckObjectivelyDown 函数定义如下:




这个函数主要进行的操作是循环查看监控此主实例的其他 Sentinel SRI_MASTER_DOWN 标志是否打开,如果打开则意味着其他特定的 Sentinel 认为主实例处于下线状态,并统计认为主实例处于下线状态的票数,如果票数大于等于主实例配置的 quorum 值,则 Sentinel 会把主实例的 SRI_O_DOWN 标志打开,并认为主实例处于客观下线状态。


sentinelStartFailoverIfNeeded 函数首先会检查实例是否处于客观下线状态(SRI_O_DOWN 标志是否打开),并且在 2 倍主实例配置的主备倒换超时时间内没有进行主备倒换工作,Sentinel 会打开 SRI_FAILOVER_IN_PROGRESS 标志并设置倒换状态为 SENTINEL_FAILOVER_STATE_WAIT_START。并开始进行主备倒换工作。主备倒换的细节将在主备倒换的章节里介绍。



Sentinel 的网络连接


上文提到每个 Sentinel 实例会维护与所监测的主从实例的两个连接,分别是命令连接(Command Connection)和发布订阅连接(Pub/Sub Connection)。但是需要注意的是,Sentinel 和其他 Sentinel 之间只有一个命令连接。下面将分别介绍命令连接和发布订阅连接的作用。


命令连接


Sentinel 维护命令连接是为了与其他主从实例以及 Sentinel 实例通过发送接收命令的方式进行通信,例如:


  1. Sentinel 会默认以每 1s 间隔发送 PING 命令给其他实例以主观判断其他实例是否下线。

  2. Sentinel 会通过 Sentinel 和主实例之间的命令连接每隔 10s 发送 INFO 命令给主从实例以得到主实例和从实例的最新信息。

  3. 在主实例下线的情况下,Sentinel 会通过 Sentinel 和从实例的命令连接发送 SLAVEOF NO ONE 命令给选定的从实例从而使从实例提升为新的主节点。

  4. Sentinel 会默认每隔 1s 发送 is-master-down-by-addr 命令以询问其他 Sentinel 节点关于监控的主节点是否下线。


在 sentinel.c 中的 sentinelReconnectInstance 函数中,命令连接的初始化如下:



发布订阅连接


Sentinel 维护和其他主从节点的发布订阅连接作用是为了获知其他监控相同主从实例的 Sentinel 实例的存在,并且从其他 Sentinel 实例中更新对所监控的主从实例以及发送的 Sentinel 实例的认知。例如在主备倒换完成后,其他 Sentinel 通过读取领头的 Sentinel 的频道消息来更新新的主节点的相关信息(地址,端口号等)。


Sentinel 在默认每隔 2 秒钟会发送 Hello 消息包到其对应的主从实例的__sentinel__:hello 频道中。Hello 消息格式如下:


__sentinel_:hello <sentinel 地址> <sentinel 端口号> <sentinel 运行 id> <sentinel 配置纪元> <主节点名字 > <主节点地址> <主节点端口号> <主节点配置纪元>


当 Sentinel 通过订阅连接收到其他 Sentinel 发送的的 Hello 包时,会更新对主从节点以及 S 发送 Sentinel 的认知,如果收到自己发送的 Hello 包,则简单的丢弃不做任何处理。这部分代码逻辑是在 sentinel.c 中的 sentinelProcessHelloMessage 函数中定义的,由于篇幅原因在这里不做详细介绍。


在 sentinel.c 中的 sentinelReconnectInstance 函数中,发布订阅连接初始化如下:



is-master-down-by-addr 命令


Sentinel 会默认每隔 1s 通过命令连接发送 is-master-down-by-addr 命令以询问其他 Sentinel 节点关于监控的主节点是否下线。另外,在主实例下线的情况下,Sentinel 之间也通过 is-master-down-by-addr 命令来获得投票并选举领头 Sentinel。is-master-down-by-addr 格式如下:


is-master-down-by-addr: <主实例地址> <主实例端口号> <当前配置纪元> <运行 ID>


如果不是在选举领头 Sentinel 过程中, <runid>项总为*,相反地,如果在 Sentinel 向其他 Sentinel 发送投票请求情况下,<runid>项为自己的运行 id。这部分代码如下:



is-master-down-by-addr 的命令回复格式如下:


  • <主节点下线状态>

  • <领头 Sentinel 运行 ID >

  • <领头 Sentinel 配置纪元>


Sentinel 在收到其他 Sentinel 命令回复后,会记录其他 Sentinel 回复的主实例在线状态信息,以及在选举领头 Sentinel 过程中的投票情况,这部分的代码逻辑定义在 sentinel.c 中的 sentinelReceiveIsMasterDownByReply 函数:



Tilt 模式


Sentinel 的 Tilt 模式会在以下两种情况下开启:


  1. Sentinel 进程被阻塞超过 SENTINEL_TILT_TRIGGER 时间(默认为 2s),可能因为进程或系统 I/O(内存,网络,存储)请求过多。

  2. 系统时钟调整到之前某个时间值。


Tilt 模式是一种保护机制,处于该模式下 Sentinel 除了发送必要的 PING 及 INFO 命令外,不会主动做其他操作,例如主备倒换,标志主观、客观下线等。但可以通过 INFO 命令及发布订阅连接的 HELLO 消息包来获取外界信息并对自身结构进行更新,直到 SENTINEL_TILT_PERIOD 时长(默认为 30s)结束为止,我们可以认为 Tilt 模式是 Sentinel 的被动模式。


判断 Tilt 模式的代码逻辑定义如下:



参考资料:


ü https://github.com/antirez/redis


ü https://redis.io/topics/sentinel


ü Redis 设计与实现第二版 黄健宏著


本文分享自华为云社区《Redis Sentinel 源码分析》,原文作者:中间件小哥。


点击关注,第一时间了解华为云新鲜技术~


发布于: 2021 年 02 月 08 日阅读数: 29
用户头像

提供全面深入的云计算技术干货 2020.07.14 加入

华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态,方便开发者快速成长与发展,欢迎提问、互动,多方位了解云计算! 传送门:https://bbs.huaweicloud.com/

评论 (1 条评论)

发布
用户头像
认真学习
2021 年 02 月 28 日 11:12
回复
没有更多了
Redis Sentinel 源码:Redis的高可用模型分析