Redis Sentinel是 Redis实现高可用(HA)的组件(非集群支持)。Reids有个叫Redis Cluster产品, Sentinel与它无关,Sentinel给无需集群环境的用户提供简单的故障转移功能。
Sentinel的基本功能:
- Monitoring : Sentinel以自动化的failover(故障转移)为目的,持续监控master/slave的状态。
- Notification: redis实例宕机,或发生failover的时候通过pub/sub功能通知客户端,也可以通过预配置的shell script实现通知管理员的功能。
- Automatic failover: master发生宕机后将开始执行failover流程,sentinel将挑选适合的slave将它提升为master,并通知其他的slave使用新的master。
- Configuration provider: Sentinel给客户端提供服务发现功能,给客户端提供master, slave的信息以提供读写分离等功能。sentinel充当的是配置提供的角色而非提供代理服务。
关于Sentinel的基本知识:
-
为提供高可用高可用最少需要3个sentinel实例,并且为了投票的效率性推荐实例数量为奇数(偶数状态下发生50%投票率将导致重新投票)。
-
各实例应发布到相对独立的VM、物理服务器或可用区(AZ),以降低故障影响。
-
Sentinel + Redis模式下,redis间的数据同步是异步方式进行的,故障转移有可能会导致数据的丢失。
-
所有构建的HA架构应做充分的验证测试,没有验证有效性的HA架构不能认定为安全的。
两种下线状态: S_DOWN, O_DOWN
- S_DOWN(Subjectively Down 主观下线) - 本地单个sentinel没有接收到redis实例的PING响应,此时会进入S_DOWN状态。
- O_DOWN(Objectively Down 客观下线) - sentinel判定某个实例为S_DOWN后,会通过is-master-down-by-addr命令询问其他sentinel对master的下线与否判定情况,当超出已设定quorum数的sentinel回复S_DOWN状态,下线状态将从S_DOWN -> 升级为ODOWN状态。
O_DOWN状态是failover的触发条件,仅适用于master实例。slave宕机时sentinel不做任何操作,当然不会有O_DOWN状态。
failover过程
Sentinel会定时对master, slave做healthcheck。
对master实例做出O_DOWN判定后(src@sentinelCheckSubjectivelyDown)将启动failover流程。
首选会选举一个执行failover操作的sentinel leader,被选为leader的sentinel会经过一系列筛选,优先级计算等操作后选出一个master后进行主备切换。
failover过程中会经过如下几个failover状态的变化。 (参考sentinel.c)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
+-------------------------------------------+
| SENTINEL_FAILOVER_STATE_NONE |
| - No failover in progress |
+-------------------------------------------+
↓
+-------------------------------------------+
| SENTINEL_FAILOVER_STATE_WAIT_START |
| - Wait for failover_start_time |
+-------------------------------------------+
↓
+-------------------------------------------+
| SENTINEL_FAILOVER_STATE_SELECT_SLAVE |
| - Select slave to promote |
+-------------------------------------------+
↓
+-------------------------------------------+
| SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOON |
| - Slave -> Master |
+-------------------------------------------+
↓
+-------------------------------------------+
| SENTINEL_FAILOVER_STATE_WAIT_PROMOTION |
| - Wait slave to change role |
+-------------------------------------------+
↓
+-------------------------------------------+
| SENTINEL_FAILOVER_STATE_RECONF_SLAVES |
| - SLAVEOF newmaster |
+-------------------------------------------+
↓
+-------------------------------------------+
| SENTINEL_FAILOVER_STATE_UPDATE_CONFIG |
| - Monitor promoted slave. |
+-------------------------------------------+
Sentinel leader 选举过程
- Sentinel对master判定为O_DOWN状态后开始failover流程。
- 将failover_state从
SENTINEL_FAILOVER_STATE_NONE
->修改为SENTINEL_FAILOVER_STATE_WAIT_START
(src@sentinelStartFailover)
- 将failover_state从
- current-epoch +1后给其他sentinel发送
SENTINEL IS-MASTER-DOWN-BY-ADDR <ip> <port> <current-epoch> <runid>
命令以请求投票(src@sentinelAskMasterStateToOtherSentinels) - 接收到投票(is-master-down-by-addr)请求的sentinel会比较请求过来的 req_epoch和自身的current-epoch。如果req_epoch大于 current-epoch将更新master结构体的leader,leader_epoch为请求过来的 req_runid, req_epoch。如果req_epoch等于current-epoch说明接收请求的sentinel已经投过票了,将返回已经投过票的sentinel的 runid。 (src@sentinelVoteLeader)
- 选举期间内一半以上sentinel投过票并最少获得quorum数的投票以后将被选举为leader,此时failover_state将变更为
SENTINEL_FAILOVER_STATE_SELECT_SLAVE
.src@sentinelFailoverWaitStart- 需最少需要获得
50% + 1
的票数才能成为leader,这也是为什么建议sentinel实例数为奇数的原因
- 需最少需要获得
- 选举期间内如果没有当选的leader,将经过 (failover_timeout * 2)时间后重新开始选举.
- 当选的leader将执行新master的选出及替换工作.
master 选出
master选出过程开始后,将首先剔除不适合当作master的slave后通过优先级的计算选择最终的master。
将通过如下条件剔除不适合的slave: (src@sentinelSelectSlave)
- 剔除S_DOWN, O_DOWN, DISCONNECTED 状态的实例
- 剔除5秒(sentinel_ping_period*5) 内没有PING响应的实例。
- 剔除slave_priority(优先顺序)为0的实例 (priority=0的当作废弃实例)
- 剔除info_validity_time为3秒以前或5秒(master为S_DOWN状态时)以前的实例
- 剔除master_link_down_time大于 (now - master->s_down_since_time) + (master->down_after_period * 10) 的实例
剔除不适合的slave以后,将通过如下顺序挑选最终的master(src@compareSlavesForPromotion )
- slave_priority小的优先
- replication offset(slave_repl_offset)大的优先
- runid小的优先
找到适合的slave后failover_state将变更为SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE。(src@sentinelFailoverSelectSlave)
slave to master
找到适合(状态最优)的slave后将开始实际的主备切换工作。
-
SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE
状态 在failover_state为SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE状态下,sentinel将发送SLAVEOF NO ONE
命令给被选的slave,让其role变更为master。而后failover_state将变更为SENTINEL_FAILOVER_STATE_WAIT_PROMOTION。(src@sentinelFailoverSendSlaveOfNoOne) -
SENTINEL_FAILOVER_STATE_WAIT_PROMOTION
状态 sentinel发送SLAVEOF NO ONE
命令后,使用INFO
命令确认slave的role是否变更为master。failover_timeout内slave变更为master以后failover_state变更为SENTINEL_FAILOVER_STATE_RECONF_SLAVES。(src@sentinelGetCurrentMasterAddress) -
SENTINEL_FAILOVER_STATE_RECONF_SLAVES로
状态 给其他slave发送SLAVEOF <new master>命令,然后failover_state变更为SENTINEL_FAILOVER_STATE_UPDATE_CONFIG
。(src@sentinelFailoverDetectEnd) -
SENTINEL_FAILOVER_STATE_UPDATE_CONFIG
状态 内存中的master信息替换为新master信息后重写redis的redis.conf文件。src@sentinelResetMasterAndChangeAddressfailover_state重新恢复为
SENTINEL_FAILOVER_STATE_NONE
,failover流程结束。
Sentinel的通信
所有sentinel有如下通信对象:
- 监控中的master
- 所有与master连接的slave
- 所有与master连接的其他sentinel
我们都知道sentinel.conf
只配置了需要监控的master信息。 那sentinel与slave跟其他sentinel是如何通信的 ?
Sentinel如何与Slave通信的?
Sentinel은 master에 INFO
명령을 날려 slave정보를 조회한다.(src@sentinelRefreshInstanceInfo)
Sentinel是通过给master发送INFO
命令查询slave的信息。(src@sentinelRefreshInstanceInfo)
Sentinel间是如何通信的?
Sentinel间的通信是通过redis的Pub/Sub功能实现的。master有个__sentinel__:hello
sentinel专用渠道,用来发布自己的ip, 端口等信息。(src@sentinelSendHello)
- 消息格式: sentinel_ip,sentinel_port,sentinel_runid,current_epoch, master_name,master_ip,master_port,master_config_epoch
sentinel读取订阅中的__sentinel__:hello
渠道的消息后将首先确认runid。runid与自己的runid一样时,消息判定为自己的发布的消息而废弃掉。runid不一样则记录其他sentinel的信息到字典中。(src@sentinelReceiveHelloMessages)