作者:Wen Hui
转载:中间件小哥
Sentinel 的选举机制
在上面的文章我们提到过,Sentinel在主备倒换的过程中会使用is-master-down-by-addr命令来与其他Sentinel进行通信以取得其他Sentinel的投票。每个Sentinel实例都有被选举成领头Sentinel的机会,领头Sentinel会主导整个主节点下线进行主备倒换的过程。
如上面代码所示,当Sentinel在主备倒换开始的时候会使用is-master-down-by-addr命令附上自己的运行Id已请求其他Sentinel实例的投票。
如果Sentinel收到其他Sentinel的投票请求,在以下两种情况下会把自己的票投给请求的Sentinel实例:
1. Sentinel的配置纪元小于或等于发送投票请求的Sentinel配置纪元。
2. 在当前配置纪元中这是收到的第一个投票请求。
这部分代码逻辑定义在sentinel.c中的sentinelVoteLeader函数中。
Sentinel会收到is-master-down-by-addr的回复,并且拿到回复中的领头Sentinel运行Id并记录在对方Sentinel的结构体中。
在主备倒换开始的时候,Sentinel会查看监控相同的主实例的其他Sentinel的投票情况,如果下面两种情况都满足,那么这个Sentinel实例将自动被选为领头Sentinel实例并进行主备倒换操作。
1. 监控相同主节点的Sentinel大于一半(50%+1)参与投票。
2. 在监控主实例时配置的quorum值大于一半Sentinel数量的情况下,Sentinel收到的投票数量达到或超过quorum值,如果quorum值小于一半Sentinel数量,Sentinel收到的投票数量大于一半Sentinel(50%+1)数量。
如果以上条件对于所有监控主实例的Sentinel不都满足,那么Sentinel会重新投票直到有Sentinel得到其他Sentinel的大多数票数为止。这部分逻辑是在sentinel.c中的sentinelGetLeader函数中定义的。
主备倒换过程
Sentinel在主实例下线时进行主备倒换的状态机定义在sentinel.c中的sentinelFailoverStateMachine函数中:
下面详细说明Sentinel在主备倒换的状态变化。
SENTINEL_FAILOVER_STATE_WAIT_START状态:
在SENTINEL_FAILOVER_STATE_WAIT_START状态下,Sentinel会计算其他节点的选举领头Sentinel的投票数量,如果这个Sentinel被选举为领头Sentinel,Sentinel会将自己状态更新为SENTINEL_FAILOVER_STATE_SELECT_SLAVE状态。如果Sentinel没有被选举为领头Sentinel,Sentinel会在选举超时的时候退出主备倒换状态。这部分代码定义在sentinel.c中的sentinelFailoverWaitStart函数中。
SENTINEL_FAILOVER_STATE_SELECT_SLAVE状态:
在SENTINEL_FAILOVER_STATE_SELECT_SLAVE状态下,领头Sentinel会选择下线主实例下面的最佳从实例提升为主实例,详细算法在sentinel.c里面的sentinelSelectSlave函数中定义:
选择最佳从实例的顺序如下:
1. 排除所有处于主观,客观下线的从实例。
2. 排除所有处于无法连接状态的从实例。
3. 排除所有没有在在默认5s内回复Sentinel的从实例。
4. 排除所有优先级为0的从实例。
5. 排除所有info_validity_time为3秒以前的,或5秒以前的(在主实例为主观下线状态下)的从实例。
6. 排除所有master_link_down_time时间大于主实例下线时间和10*down_after_period的和的从实例。
以上排除过程以后,从实例会按照以下条件进行排序(优先级从上到下):
1. 从实例优先级
2. 从实例复制偏移量。
3. 从实例Id值。
排序后的第一个从实例会被提升为主实例,Sentinel会过渡到SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE状态,如果没有符合要求的从实例选出, Sentinel会退出主备倒换过程。
SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE状态:
在SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE状态中,领头Sentinel会发送slaveof no one 命令给选出的最佳从实例,并且Sentinel会把状态更新为SENTINEL_FAILOVER_STATE_WAIT_PROMOTION。
SENTINEL_FAILOVER_STATE_WAIT_PROMOTION状态:
在SENTINEL_FAILOVER_STATE_WAIT_PROMOTION状态中,Sentinel会不断等待升级为主实例的从实例的INFO回复,并检查升级的从实例是否改变自己的角色为主实例。如果在配置的Failover-timeout时间内没有收到从实例报告的角色变换,Sentinel会终止主备倒换过程。
相反的,如果Sentinel收到从实例的INFO消息并检测出角色变换成功,Sentinel会改变主备倒换状态到SENTINEL_FAILOVER_STATE_RECONF_SLAVES。
SENTINEL_FAILOVER_STATE_RECONF_SLAVES状态:
在SENTINEL_FAILOVER_STATE_RECONF_SLAVES状态下,领头Sentinel会发送slaveof <promoted-slave-ip> <promoted-slave-port> 给其他的从实例:
主备倒换过程末尾阶段,Sentinel会检查是否其他所有的从实例收到并成功配置新的主实例,并将主备倒换状态更新为SENTINEL_FAILOVER_STATE_UPDATE_CONFIG。
SENTINEL_FAILOVER_STATE_UPDATE_CONFIG状态:
最后,在Sentinel处于SENTINEL_FAILOVER_STATE_UPDATE_CONFIG状态下,Sentinel会更新主实例信息为提升为新的主节点的从实例信息,并会在旧的主实例重新上线的时候通知其变为新主实例的一个从实例,这就是整个Sentinel进行主备倒换的过程。
整个主备倒换过程如下图所示:
参考资料:
https://github.com/antirez/redis
https://redis.io/topics/sentinel
Redis设计与实现第二版黄健宏著