Redis 哨兵简介
哨兵(Sentinel)是一个分布式的系统,一个架构中可以运行多个哨兵进程,他们之间使用gossip protocols来进行通信,它为Redis提供了高可用的方案,主要是通过管理多个Redis服务器来完成以下三个任务。
- 监控(Monitoring), 哨兵会定期检查主服务器和从服务器的运作是否正常。
- 提醒(Notification), 某个Redis服务器出现问题的时候,会主动发出通知
- 自动故障迁移(Automaticfailover),当监控的主服务器失效时,哨兵会进行自动故障迁移,将某一个从服务器升级为主服务器,使得新主服务器能够代替失效的服务器。
哨兵机制
哨兵部署
上图是一份典型的Reids部署方式,部署哨兵时一般都会部署三个及以上的哨兵,其原因是哨兵是保护Redis不出现单点故障的机制,如果哨兵只部署1个,那么哨兵自己就成为了单点;如果部署两个哨兵,那么master出现故障的时候,哨兵之间必须约定好谁来执行主从切换,这就涉及到选举的问题了,而两个哨兵之间是无法完成选举的。所以哨兵的个数需要大于等于三个,且最好是奇数。
再来看一份哨兵启动的典型配置文件
// 监控一个名称为 mymaster的Redis 主服务器,其 ip 127.0.0.1 端口 6379 quorum 2
sentinel monitor mymaster 127.0.0.1 6379 2
// 如果哨兵在60s内没收到mymaster的有效ping回复,则认为该master处于down状态, 即主观下线
sentinel down-after-milliseconds mymaster 60000
// mymaster 自动故障迁移超时时间为 180s
sentinel failover-timeout mymaster 180000
// 下面的配置和上面一致,只是换了 地址、名字 和 quorum
sentinel monitor quest 192.168.1.3 6380 4
sentinel down-after-milliseconds quest 10000
sentinel failover-timeout quest 180000
其中,quorum 在哨兵中会有两处用到:
- 当哨兵在一定时间内没有收到其监控的主服务器的有效ping回复的话,哨兵会认为该服务器处于主观下线状态,当quorum 配置成2 的话,就是说明当至少有2个哨兵认为该主服务器处于主观下线状态的时候,该主服务器就被认为是客观下线状态。
- 当主从切换开始之后,哨兵之间会进行选举,选举出一个主哨兵进行主从切换,当quorum配置成4的话,说明一个哨兵至少需要4票才能成为主哨兵。
在这份配置中,我们可以看到只是设置了一下需要监控的服务器地址和一些必要的参数,并没有涉及从服务器(Redis Slave)以及其他哨兵的配置。那么从服务器和其他哨兵是如何被发现的呢。
哨兵执行流程
哨兵的启动
首先,通过下述方式可以启动一个哨兵
redis-server sentinel.conf --sentinel
启动哨兵后,哨兵会根据配置进行一系列的初始化,然后连接对应的主服务器。哨兵会与主服务器建立两条连接,一条命令连接,一条订阅连接(也可以成为消息连接),
如上图,各个启动的哨兵都于主服务器建立了两条连接,其中虚线是命令连接,实线是订阅连接。订阅连接建立后,哨兵会订阅Redis服务的__sentinel__::hello频道。
哨兵的定时任务
在命令连接上,哨兵会向主服务器发送命令,以完成定时监控的任务
- info命令的采集
每一个哨兵都会每隔10s向连接的服务器都发送info命令,以此采集其服务器信息以及节点拓扑结构。所以配置的时候,只配置了主服务器的地址信息。之后从服务器信息可以通过info命令获得。
当收到info命令的回包后,会与未建立连接的其他从服务器也分别建立命令连接和订阅连接
- 哨兵信息的发布
每个哨兵会在每2s会通过命令连接向主节点和从节点的频道内发布一条信息,从前向后的格式依次如下
字段名 | 字段意义 |
sentinel_ip | 哨兵IP |
sentinel_port | 哨兵端口 |
sentinel_runid | 哨兵ID |
current_epoch | 当前纪元(用于选角和主从切换) |
master_name | master的名称 |
master_ip | master的ip |
master_port | master的端口 |
master_config_epoch | master的纪元(用于选举和主从切换) |
每个哨兵都会通过订阅连接接收到这条发布的消息,可以根据里面的字段确定是否是新加入的哨兵,以及其ip地址,如果是新加入的哨兵,所有哨兵都会与它进行连接,不过与服务器连接不同,哨兵之间只会建立命令连接。
- ping命令
哨兵会每1s向所有其他哨兵、所有的redis服务器发送ping命令用于心跳检测,如果该命令在有效时间内为得到回复,则会被该哨兵认为是主观下线。这个命令也是哨兵节点用来判断节点是否正常的重要依据
主从切换
再提一次两个概念,主观下线和客观下线
主观下线: 前面有提到过了,只有在一定时间内没有收到ping命令的回复,则发出ping命令的哨兵会认为对应的主服务器节点下线,这就是主观下线。一定时间是在配置中的down-after-milliseconds字段。
客观下线:当超过一定数量的哨兵认为主服务器节点处于主观下线状态的话,那么该主服务器节点就会被认为是客观下线。这边的一定数量是在配置中的quorum 字段。
当主服务器节点处于主观下线阶段,为了保证Redis的高可用性,哨兵就会开始主从切换流程。
主从切换状态
先定义主从切换间的几个状态
状态名 | 状态值 | 意义 |
SENTINEL_FAILOVER_STATE_NONE | 0 | 没发生主从切换 |
SENTINEL_FAILOVER_STATE_WAIT_START | 1 | 等待开始主从切换,(哨兵之间正在选择主节点) |
SENTINEL_FAILOVER_STATE_WAIT_SELECT_SLAVE | 2 | 选择一台从服务器作为新的主服务器节点 |
SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE | 3 | 将选择的服务器提升成主服务器节点 |
SENTINEL_FAILOVER_STATE_WAIT_PROMOTION | 4 | 等待被选中的服务器上报状态 |
SENTINEL_FAILOVER_STATE_RECONF_SLAVES | 5 | 将其他Slave的同步数据服务器切换为新的主服务器 |
SENTINEL_FAILOVER_STATE_UPDATE_CONFIG | 6 | 重置主服务器节点的IP端口为新的主服务器 |
而整个主从切换的状态转换图如下
主从切换流程
- 初始状态时,系统的状态如下
- 当一个哨兵发现主节点处于主观下线阶段时,会将自身状态切换从SENTINEL_FAILOVER_STATE_NONE 切换到SENTINEL_FAILOVER_STATE_WAIT_START ,并且将当前选举纪元加1,并开始给其他哨兵发送投票命令,让其他哨兵给自己投票。当从哨兵中选举出主哨兵之后,接下来的操作都会由该主哨兵进行。
- 主哨兵选择出来后,主哨兵会将自己的状态切换到SENTINEL_FAILOVER_STATE_WAIT_SELECT_SLAVE ,然后开始挑选一台从服务器作为主服务器,挑选规则如下:
1) 如果该Slave处于主观下线中,则不能被选中
2) 如果该Slave与主服务器断开时间过长,则不能被选中
3) 如果该Slave的slave-priority为0,则不能被选中,该字段可以在配置中设置,配置值需要是正整数且越小优先级越高。
4) 选择优先级最高的Slave,如果优先级相同,则选择具有较大复制偏移量的;如果偏移量也相同,则按字母序选择 - 选中下一台主服务器后,主哨兵会将状态切换到SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE ,并向Slave发送如下命令
MULTI
SLAVEOF NO ONE // 关闭该从服务器的复制功能,将其提升称为一个主服务器
CONFIG REWRITE // 重写配置
CLIENT KILL TYPE normal // 断开当前连接的客户端,客户端会触发重连,重连时会得到新的主服务器地址
EXEC
发送完命令后,哨兵会将自己的状态切换到 SENTINEL_FAILOVER_STATE_WAIT_PROMOTION。
- 哨兵切换到SENTINEL_FAILOVER_STATE_WAIT_PROMOTION 后,会再下一次info命令的时候检查对应从服务器的role字段,如果role是master的话,则说明从服务器已经变更了自己的角色,成为了主服务器,这时哨兵的状态就会切换到SENTINEL_FAILOVER_STATE_RECONF_SLAVES ,并将其他从服务器发送切换主服务器的命令,命令如下
MULTI
SLAVEOF IP PORT // 指明该从服务器应该向对应IP:PORT同步数据
CONFIG REWRITE // 配置重写
CLIENT KILL TYPE normal // 断开当前连接的客户端,客户端会触发重连,重连时会得到新的主服务器地址
EXEC
- 当所有其他从服务器更新完后,哨兵会将状态切换到SENTINEL_FAILOVER_STATE_UPDATE_CONFIG ,这个步骤中,主哨兵会将原先下线的主服务器切换到当前新的主服务器,然后将原先的主服务器变更成当前主服务器的从服务器。
- 完成上述所有步骤后,主哨兵会将状态切换到SENTINEL_FAILOVER_STATE_NONE,至此,主从切换完成。
一些问题
通过上述对主动切换的介绍,思考以下问题
- 主从切换完成后,其他哨兵如何知道现在提供服务的主节点时哪个?
- 如果执行主动切换的主哨兵在中途发送故障了,切换操作是否会由其他哨兵继续呢?
- 故障的master恢复之后,是否还能继续提供服务呢?
答案放在文末,就不和问题放在一起了
参考
- Eugene_Jin
- 詠聖wK
- 散尽浮华
- 《Redis5 设计与源码分析》 陈雷
问题答案
- 其余哨兵可以通过subscribe sentinel::hello 频道知道当前提供服务的主节点的IP和端口
- 执行哨兵发生故障后,其他哨兵会重新从选举哨兵开始流程
- 宕机的主节点恢复后会作为新的主节点的从节点提供服务