11 Sentinel
Sentinel
Sentinel(哨兵)是Redis的高可用性解决方案,由一个或多个Sentinel实例组成的Sentinel系统可以监视任意多个master以及属下的所有slave。Sentinel在被监视的master下线后,自动将其属下的某个slave升级为新的master,然后由新的master继续处理命令请求。
1 Sentinel初始化过程
启动并初始化Sentinel
启动一个Sentinel可以使用命令:
redis-sentinel sentinel.conf
或者
redis-server sentnel.conf —sentinel
当一个Sentinel启动时,会执行以下几步:
- 初始化服务器
- 将普通Redis服务器使用的代码替换成Sentinel专用代码
- 初始化Sentinel状态
- 根据配置文件,初始化监视的master列表
- 创建与master的网络连接
初始化服务器
Sentinel本质上是一个运行在特殊模式下的Redis服务器,它的初始化过程与普通Redis服务器并不相同:
| 功能 | Sentinel使用情况 |
|---|---|
数据库和键值对方面的命令:SET, DEL, FLUSHDB |
不使用 |
| 事务命令 | 不使用 |
| 脚本命令 | 不使用 |
| RDB和AOF持久化 | 不使用 |
| 复制命令 | Sentinel内部使用,客户端不可用 |
| 发布、订阅命令 | 订阅命令可在Sentinel内部和客户端使用,发布命令只能在Sentinel内部使用 |
| 文件事件处理器(发送命令请求,处理命令回复) | Sentinel内部使用 |
| 时间事件处理器 | Sentinel内部使用,serverCron会用sentinel.c/sentinelTimer函数 |
使用Sentinel专用代码
将一部分普通Redis服务器的代码替换为Sentinel专用代码,比如端口号,命令表。
初始化Sentinel状态
接下来,服务器会初始化一个sentinel.c/sentinelState结构,它保存了服务器有关Sentinel的状态:
1 | struct sentinelState { |
初始化Sentinel状态的masters属性
sentinelRedisInstance结构代表一个被监视的Redis服务器实例,可以是master、slave、或者另一个Sentinel。
1 | typedef struct sentinelRedisInstance { |
sentinelRedisInstance.addr指向一个sentinel.c/sentinelAddr结构,它保存着实例的IP地址和端口号:
1 | typedef struct sentinelAddr { |
创建与master的网络连接
连接建立后,Sentinel将成为master的客户端,可以向其发送命令。对于被监视的master来说,Sentinel会创建两个异步网络连接:
- 命令连接,用于发送和接收命令。
- 订阅连接。用于订阅master的
__sentinel__:hello频道。
2 信息传递过程
获取master信息
Sentinel以默认10秒一次的频率,向master发送INFO命令,获取其当前信息:
- master本身的信息,包括运行ID、role等。据此,Sentinel更新master实例的结构。
- master的slave信息。据此,Sentinel更新master实例的slaves字典。
获取slave信息
Sentinel发现master有新的slave时,除了会为这个slave创建相应的实例结构外,还会创建到它的命令连接和订阅连接。
通过命令连接,Sentinel会向slave每10秒发送一次INFO命令,根据回复更新slave的实例结构:
- slave的运行ID
- slave的角色role
- master的地址和端口
- 主从的连接状态
- slave的优先级
- slave的复制偏移量
向master和slave发送信息
默认情况下,Sentinel会以两秒一次的频率,通过命令连接向所有被监视的master和slave发送:
PUBLISH __sentinel__:hello “
, , , , , , , “
其中以s_开头的参数表示Sentinel本身的信息,m_开头的参数是master的信息。如果Sentinel正在监视的是slave,那就是slave正在复制的master信息。
接收来自master和slave的频道信息
当Sentinel与一个master或slave建立订阅连接后,会向服务器发送以下命令:
SUBSCRIBE __sentinel__:hello
Sentinel对__sentinel__:hello频道的订阅会持续到两者的连接断开为止。也就是说,Sentinel既可以向服务器的__sentinel__:hello频道发送信息,又通过订阅连接从__sentinel__:hello频道接收信息。
对于监视同一个server的多个Sentinel来说,一个Sentinel发送的信息会被其他Sentinel收到。这些信息用于更新其他Sentinel队发送信息Sentinel和被监视Server的认知。
更新sentinels字典
Sentinel为master创建的实力结构中,有sentinels字典保存了其他监视这个master的Sentinel:
- 键是Sentinel名字,格式为ip: port。
- 值是Sentinel实例的结构。
当一个Sentinel收到其他Sentinel发来的信息时,目标Sentinel会从信息中提取出:
- 与Sentinel有关的参数:源Sentinel的IP、端口、运行ID、配置纪元。
- 与master有关的参数:master的名字、IP、端口、配置纪元。
根据提取的参数,目标Sentinel会在自己的Sentinel状态中更新sentinels和masters字典。
创建连向其他Sentinel的命令连接
Sentinel通过频道信息发现一个新的Sentinel时,不仅会为其创建新的实例结构,还会创建一个连向新Sentinel的命令连接,新的Sentinel也会创建连向这个Sentinel的命令连接,最终,监视同一master的多个Sentinel成为相互连接的网络。各个Sentinel可以通过发送命令请求来交换信息。
3 Sentinel检测状态
检测主观下线状态
默认情况下,Sentinel会每秒一次地向所有与它创建了嘛命令连接的实例(master、slave、其他sentinel)发送PING命令,并通过回复来判断其是否在线。只有+PONG/-LOADING/-MASERDOWN三种有效回复。
Sentinel的配置文件中down-after-milliseconds选项指定了判断实例主观下线所需的时间长度。在down-after-milliseconds毫秒内,如果连续返回无效回复,那么Sentinel会修改这个实例对应的实例结构,将flags属性中打开SRI_S_DOWN标识,标识主观下线。
注意:多个Sentinel设置的down-after-milliseconds可能不同。
检查客观下线时长
当Sentinel将一个master判断为主观下线后,为了确认是真的下线,会向监视这一master的其他Sentinel询问。有足够数量(quorum)的已下线判断后,Sentinel会将master判定为客观下线,并对master执行故障转移。
选举领头Sentinel
master被判定为客观下线后,监视这个master的所有Sentinel会进行协商,选举一个领头Sentinel,并由其对该master执行故障转移。选举的规则如下:
- 所有Sentinel都可以成为领头。
- 每次进行领头Sentinel选举后,不论选举是否成功,所有Sentinel的配置纪元都会+1。这个配置纪元就是一个计数器。
- 一个配置纪元里,所有Sentinel都有一次将某个Sentinel设置为局部领头Sentinel的机会,且局部领头一旦设定,在这个配置纪元内就不可修改。
- 每个发现master进入客观下线的Sentinel都会要求其他Sentinel将自己设为局部领头Sentinel。
- 当一个Sentinel向另一个Sentinel发送
SENTINEL is-master-down-by-addr,且命令中的runid参数是自己的运行ID,这表明源Sentinel要求目标Sentinel将他设置为局部领头。 - Sentinel设置局部领头的规则是先到先得。
- 目标Sentinel收到
SENTINEL is-master-down-by-addr后,会返回一条命令回复,恢复中的leader_runid和leader_epoch参数分别记录了目标Sentinel的局部领头Sentinel的运行ID和配置纪元。 - 源Sentinel收到目标Sentinel的回复后,检查回复中的
leader_runid和leader_epoch是否和自己相同。 - 如果某个Sentinel被半数以上的Sentinel设置为局部领头,那么这个Sentinel就成为领头Sentinel。
- 因为领头Sentinel需要半数以上的支持,且每个Sentinel在每个配置纪元里只设置一次局部领头,所以一个配置纪元里,只能有一个领头。
- 如果给定时限内,没有产生领头Sentinel,那么各个Sentinel过段时间再次选举,知道选出领头为止。
故障转移
领头Sentinel会对已下线的master执行故障转移,包括以下三个步骤:
- 从已下线master属下的所有slave选出一个新的master。
- 让已下线master属下的所有slave改为新复制新的master。
- 让已下线master成为新master的slave,重新上线后就是新slave。
选出新的master
新master的挑选规则:
- 在线
- 五秒内回复过领头Sentinel的
INFO命令 - 与已下线master在
down-after-milliseconds毫秒内有过通信。 - salve的自身有优先级
- 复制偏移量最大
Sentinel向salve发送SLAVEOF no one命令将其转换为master。
修改salve的复制目标
同样通过SLAVEOF命令实现。
将旧的master变为slave
同样通过SLAVEOF命令实现。








