参考资料:
《渐进式解析 Redis 源码 - 哨兵 sentinel》
《深入剖析Redis系列- Redis哨兵模式与高可用集群》
前文:
写在开头:本文为学习后的总结,可能有不到位的地方,错误的地方,欢迎各位指正。
目录
Redis 的主从复制模式下,一旦主节点由于故障不能提供服务,需要手动将从节点晋升为 主节点,同时还要通知客户端更新主节点地址,这种故障处理方式从一定程度上是无法接受的。Redis 2.8 以后提供了 Redis Sentinel 哨兵机制 来解决这个问题。
Redis 哨兵(Sentinel)是由一个或多个 Sentinel 实例组成的 Sentinel 系统,可以监视任意多个主服务器,以及这些主服务器的所有从服务器。
Sentinel 本质上是一个运行在特殊状模式下的 Redis 服务器。Sentinel 模式下 Redis 服务器只支持 PING、SENTINEL、INFO、SUBSCRIBE、UNSUBSCRIBE、PSUBSCRIBE、PUNSUBSCRIBE 七个命令。
Redis官方文档的描述如下:
其中,监控和自动故障转移功能,使得哨兵可以及时发现主节点故障并完成转移;而配置提供者和通知功能,则需要在与客户端的交互中才能体现。
当一个 Sentinel 启动时,会创建连向被监视的主服务器的网络连接。它可以向主服务器发送命令,并从命令回复中获取相关的信息。对于每个被 Sentinel 监视的主服务器,Sentinel 会创建两个连向主服务器的异步网络:
命令连接:用于向主服务器发送命令(包括ping、info),并接受命令回复,用来获取从节点的信息与Sentinel 、Redis节点的状态信息。
订阅连接:用于订阅主服务器的 __sentinel__:hello 频道,用来和其他Sentinel 实例建立连接。
Sentinel实例会向与其连接的节点(包括主从节点)的 __sentinel__:hello 频道发送消息, 并订阅该频道(SUBSCRIBE __sentinel__:hello)。这样一来,当后续的Sentinel实例与主库建立连接,发送消息时,之前订阅该频道的消息就能够收到新实例加入的消息,然后就可以从这个频道直接获取新实例的信息并建立网络连接。
以下图为例,哨兵 1 把自己的 IP(172.16.19.3)和端口(26579)发布到__sentinel__:hello频道上,哨兵 2 和 3 订阅了该频道。那么此时,哨兵 2 和 3 就可以从这个频道直接获取哨兵 1 的 IP 地址和端口号。然后,哨兵 2、3 可以和哨兵 1 建立网络连接。
Sentinel 对 __sentinel__:hello 频道的订阅会一直持续到 Sentinel 与服务器断开连接为止。在在此之前Sentinel 会以每两秒一次的频率,通过命令向所有被监视的主服务器和从服务器发送自己的信息及主节点相关的配置。
Sentinel 向主服务器发送 INFO 命令,获取主服务器及它的从服务器信息。
Sentinel 默认会以每十秒一次的频率,通过命令连接向被监视的主服务器发送 INFO 命令,并通过分析 INFO 命令的回复来获取主服务器的当前信息。
当 Sentinel 发现主服务器有新的从服务器出现时,Sentinel 除了会为这个新的从服务器创建相应的实例结构之外,Sentinel 还会创建连接到从服务器的命令连接和订阅连接。
Sentinel 向 Redis 服务器发送 PING 命令,检查其状态。
每个 Sentinel 节点会以 每秒一次 的频率对 Redis 节点和 其它 的 Sentinel 节点发送 PING 命令,并通过节点的 回复 来判断节点是否在线。哨兵正是通过这个机制得以判断主节点是否下线,这将涉及到2个新的改变,主观下线与客观下线。
注意:一个有效的 PING 回复可以是:+PONG、-LOADING 或者 -MASTERDOWN。如果 服务器 返回除以上三种回复之外的其他回复,又或者在 指定时间 内没有回复 PING 命令, 那么 Sentinel 认为服务器返回的回复 无效(non-valid)。
在上文中,我们留了2个概念(主观下线与客观下线)没有解释,这一节我们结合哨兵对故障的处理进行讲解。
主观下线适用于所有 主节点 和 从节点。如果在 down-after-milliseconds 毫秒内,Sentinel 没有收到 目标节点 的有效回复,则会判定 该节点 为 主观下线。
客观下线只适用于主节点。当 Sentinel 将一个主服务器判断为主管下线后,为了确认这个主服务器是否真的下线,会向同样监视这一主服务器的其他 Sentinel 询问( 通过sentinel is-master-down-by-addr指令判断),看它们是否也认为主服务器已经下线。
其他哨兵会根据自己和主库的连接情况,做出 Y 或 N 的响应,Y 相当于赞成票,N 相当于反对票。如果赞成票数(这里是2)是大于等于哨兵配置文件中的 quorum 配置项(比如quorum=2), 则可以判定主库客观下线了。当足够数量的 Sentinel 认为主服务器已下线,就判定其为客观下线,并对其执行故障转移操作。
当一个主服务器被判断为客观下线时,监视这个下线主服务器的各个 Sentinel 会进行选举,选举出一个领头的 Sentinel,并由领头 Sentinel 对下线主服务器执行故障转移操作。
哨兵的选举机制使用Raft(Raft算法详解)选举算法: 选举的票数大于等于num(sentinels)/2+1时,将成为领导者,如果没有超过,则继续选举。任何一个想成为 Leader 的哨兵,要满足两个条件:
当有3 个哨兵时,quorum 设置为 2,那么,任何一个想成为 Leader 的哨兵只要拿到 2 张赞成票,就可以了。
主库被判定为客观下线后,就需要从剩余的从库中选择一个新的主库。
然后, Sentinel Leader 先选出优先级最高的从服务器;如果优先级一样高,再选择复制偏移量最大的从服务器;如果结果还不唯一,则选出运行 ID 最小的从服务器。
在上文中,Sentinel Leader 已经挑选出了新的主节点,接下来就要进行故障转移了。
首先向这个从节点发送 SLAVEOF no one 命令,将其转换为主节点(5.0 中应该是replicaof no one)。然后Sentinel Leader 会向所有从节点发送 SLAVEOF 命令,让所有从节点指向新的主节点并复制新的主节点上的数据,然后通知客户端主节点已更换,最后将旧的主服务器标记为从服务器。当旧的主服务器重新上线,Sentinel Leader 会向它发送 SLAVEOF 命令,让其成为从服务器。
最后,需要注意的是,主从切换并不一定就能完成,下面举个例子,Redis 1主4从,5个哨兵,哨兵配置quorum为2,如果3个哨兵故障,此时主节点宕机。
由于quorum=2,所以当一个哨兵判断主库“主观下线”后,询问另外一个哨兵后也会得到同样的结果,2个哨兵都判定“主观下线”,达到了quorum的值,因此,哨兵集群可以判定主库为“客观下线”。
但哨兵不能完成主从切换。哨兵标记主库“客观下线后”,在选举“哨兵领导者”时,一个哨兵必须拿到超过多数的选票(5/2+1=3票)。但目前只有2个哨兵活着,无论怎么投票,一个哨兵最多只能拿到2票,永远无法达到N/2+1选票的结果。