第九章 哨兵
之前我们已经介绍过了Redis的复制,在这种主从模式下,如果主节点挂了,需要手工去把从节点切换为主节点,然后通知应用程序去更新主节点地址。对于生产环境,这是不可以接受的。从Redis2.8开始,我们可以使用哨兵(Redis Sentinel)来解决这个痛点。下面,我们就来介绍下哨兵吧。
9.1基本概念
Redis Sentinel 是Redis的一个高可用实现方案,下面我们先分析下主从复制模式存在些什么问题,然后深入了解Redis Sentinel的基本架构、优势,以及如何实现高可用的。
9.1.1 主从复制的问题
在Redis的主从复制模式中,从节点的作用主要有两个:一是作为主节点的备份,在主节点挂了之后,从节点可以顶上;二是可以拓展主节点读的能力。
虽然说主节点挂了之后,从节点可以顶上,但是这个步骤需要人工去干预处理的。此外,主从复制还是没有解决主节点写能力和存储能力受到单机限制的问题,这个在后面的集群会有介绍。
9.1.2 高可用
下面介绍下主节点挂了从节点顶上的步骤:
- 现在有一主二从,主节点挂了,两个从节点都连接不上主机点。
- 我们选定一个从节点(slave-1)为新的主节点,执行 slaveof no one。
- 在slave-1成为主节点之后,通知应用,让其更新主节点的信息。
- 在另外一个从节点(slave-2)执行命令,让其复制新的主节点。
- 原来的主节点恢复之后,让它去复制新的主节点。
上述处理过程就可以认为整个服务或者架构的设计不是高可用的,因为整个故障转移的过程需要人介入。考虑到这点,有些公司把上述流程自动化了,但是仍然存在如下问题:第一,判断节点不可达的机制是否健全和标准。第二,如果有多个从节点,怎样保证只有一个被晋升为主节点。第三,通知客户端新的主节点机制是否足够健壮。Redis Sentinel正是用于解决这些问题。
9.1.3 Redis Sentinel的高可用性
Redis Sentinel是一个分布式架构,其中包含若干个Sentinel节点和Redis数据节点,每个Sentinel节点会对数据节点和其余Sentinel节点进行监控,当它发现节点不可达时,会对节点做下线标识。如果被标识的是主节点,它还会和其他Sentinel节点进行“协商”,当大多数Sentinel节点都认为主节点不可达时,它们会选举出一个Sentinel节点来完成自动故障转移的工作,同时会将这个变化实时通知给Redis应用方。整个过程完全是自动的,不需要人工来介入,所以这套方案很有效地解决了Redis的高可用问题。
如下图所示,Redis Sentinel与Redis主从复制模式只是多了若干Sentinel节点,所以Redis Sentinel并没有针对Redis节点做了特殊处理,这里是很多开发和运维人员容易混淆的。
![](https://img.haomeiwen.com/i18877507/6639afbbecc84037.png)
从逻辑架构上看,Sentinel节点集合会定期对所有节点进行监控,特别是对主节点的故障实现自动转移。
下面以1个主节点、2个从节点、3个Sentinel节点组成的Redis Sentinel为例子进行说明,拓扑结构如图9-7所示。
![](https://img.haomeiwen.com/i18877507/bcbb0d69cfbedc67.png)
整个故障转移的处理逻辑有下面4个步骤:
- 主节点出现故障,此时两个从节点与主节点失去连接,主从复制失败。
- 每个Sentinel节点通过定期监控发现主节点出现了故障。
- 多个Sentinel节点对主节点的故障达成一致,选举出sentinel-3节点作为领导者负责故障转移。
- Sentinel领导者节点执行了故障转移,整个过程和9.1.2节介绍的是完全一致的,只不过是自动化完成的。
故障转移后整个Redis Sentinel的拓扑结构图9-12所示。
![](https://img.haomeiwen.com/i18877507/caac742cf26b58e2.png)
通过上面介绍的Redis Sentinel逻辑架构以及故障转移的处理,可以看出Redis Sentinel具有以下几个功能:
- 监控:Sentinel节点会定期检测Redis数据节点、其余Sentinel节点是否可达。
- 通知:Sentinel节点会将故障转移的结果通知给应用方。
- 主节点故障转移:实现从节点晋升为主节点并维护后续正确的主从关系。
- 配置提供者:在Redis Sentinel结构中,客户端在初始化的时候连接的是Sentinel节点集合,从中获取主节点信息。
同时看到,Redis Sentinel包含了若个Sentinel节点,这样做也带来了两个好处:
- 对于节点的故障判断是由多个Sentinel节点共同完成,这样可以有效地防止误判。
- Sentinel节点集合是由若干个Sentinel节点组成的,这样即使个别Sentinel节点不可用,整个Sentinel节点集合依然是健壮的。
但是Sentinel节点本身就是独立的Redis节点,只不过它们有一些特殊,它们不存储数据,只支持部分命令。下一节将完整介绍Redis Sentinel的部署过程,相信在安装和部署完Redis Sentinel后,读者能更清晰地了解Redis Sentinel的整体架构。
9.2 安装和部署
9.2.1 部署拓扑结构
下面将以3个Sentinel节点、1个主节点、2个从节点组成一个RedisSentinel进行说明,拓扑结构如图9-13所示。
![](https://img.haomeiwen.com/i18877507/34ec1bb24de4f720.png)
9.2.2 部署Redis数据节点
9.1节提到过,Redis Sentinel中Redis数据节点没有做任何特殊配置,按照之前章节介绍的方法启动就可以,下面以一个比较简单的配置进行说明
1.启动主节点:
配置:
redis_6379.conf
port 6379
daemonize yes
logfile "/data/redis/logs/redis_6379.log"
dbfilename "dump_6379.rdb"
dir /data/redis/data
启动主节点:
redis-server redis_6379.conf
2. 启动两个从节点
配置:
两个从节点的配置完全一样的,和主节点不同的地方是添加了slaveof的配置。
##redis_6381.conf类似这个
redis_6380.conf
port 6380
daemonize yes
logfile "/data/redis/logs/redis_6380.log"
dbfilename "dump_6380.rdb"
dir /data/redis/data
slaveof 127.0.0.1 6379
启动两个从节点:
redis-server redis_6380.conf
redis-server redis_6381.conf
3.确认主从关系
在主节点上
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=333914,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=333914,lag=1
...
此时拓扑结构如图9-15所示。
![](https://img.haomeiwen.com/i18877507/1a594a0b629cc3f7.png)
9.2.3 部署Sentinel节点
3个Sentinel节点的部署方法是完全一致的(端口不同),下面以sentinel-1节点的部署为例子进行说明。
1.配置Sentinel节点
redis-sentinel_26379.conf
port 26379
daemonize yes
logfile "/data/redis/logs/redis_26379.log"
dir /data/redis/data
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
1)Sentinel节点的默认端口是26379。
2)sentinel monitor mymaster127.0.0.1 6379 2 配置代表sentinel-1节点需要监控127.0.0.1:6379这个主节点,2代表判断主节点失败至少需要2个Sentinel节点同意,mymaster是主节点的别名,其余Sentinel配置将在下一节进行详细说明。
2.启动Sentinel节点
#另外两个redis-sentinel也同样启动
redis-sentinel redis-sentinel_26379.conf
3.确认
Sentinel节点本质上是一个特殊的Redis节点,所以也可以通过info命令来查询它的相关信息,从下面info的Sentinel片段来看,Sentinel节点找到了主节点127.0.0.1:6379,发现了它的两个从节点,同时发现Redis Sentinel一共有3个Sentinel节点。这里只需要了解Sentinel节点能够彼此感知到对方,同时能够感知到Redis数据节点就可以了:
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=3
当三个Sentinel节点都启动后,整个拓扑结构如图9-16所示。
![](https://img.haomeiwen.com/i18877507/77bf695e2fac018f.png)
至此Redis Sentinel已经搭建起来了,整体上还是比较容易的,但是有2点需要强调一下:
- 生产环境中建议Redis Sentinel的所有节点应该分布在不同的物理机上。
- Redis Sentinel中的数据节点和普通的Redis数据节点在配置上没有任何区别,只不过是添加了一些Sentinel节点对它们进行监控。
9.2.4 配置优化
在redis的安装目录下有sentinel.conf 这样的一个文件,里面有介绍sentinel的大概参数,下面来介绍几个重要的配置参数。
1. sentinel monitor
配置如下:
sentinel monitor <master-name> <ip> <port> <quorum>
Sentinel节点会定期监控主节点,所以从配置上必然也会有所体现,本配置说明Sentinel节点要监控的是一个名字叫做<master-name>,ip地址和端口为<ip> <port>的主节点。<quorum>代表要判定主节点最终不可达所需要的票数。但实际上Sentinel节点会对所有节点进行监控,但是在Sentinel节点的配置中没有看到有关从节点和其余Sentinel节点的配置,那是因为Sentinel节点会从主节点中获取有关从节点以及其余Sentinel节点的相关信息,有关这部分是如何实现的,将在9.5节介绍。
2. sentinel down-after-milliseconds
配置如下:
sentinel down-after-milliseconds <master-name> <times>
每个Sentinel节点都要通过定期发送ping命令来判断Redis数据节点和其余Sentinel节点是否可达,如果超过了down-after-milliseconds配置的时间且没有有效的回复,则判定节点不可达,<times>(单位为毫秒)就是超时时间。这个配置是对节点失败判定的重要依据。
优化说明:down-after-milliseconds越大,代表Sentinel节点对于节点不可达的条件越宽松,反之越严格。条件宽松有可能带来的问题是节点确实不可达了,那么应用方需要等待故障转移的时间越长,也就意味着应用方故障时间可能越长。条件严格虽然可以及时发现故障完成故障转移,但是也存在一定的误判率。
3. sentinel parallel-syncs
配置如下:
sentinel parallel-syncs <master-name> <nums>
当Sentinel节点集合对主节点故障判定达成一致时,Sentinel领导者节点会做故障转移操作,选出新的主节点,原来的从节点会向新的主节点发起复制操作,parallel-syncs就是用来限制在一次故障转移之后,每次向新的主节点发起复制操作的从节点个数。如果这个参数配置的比较大,那么多个从节点会向新的主节点同时发起复制操作,尽管复制操作通常不会阻塞主节点,但是同时向主节点发起复制,必然会对主节点所在的机器造成一定的网络和磁盘IO开销。当parallel-syncs=1时,从节点会轮询发起复制。
4. sentinel failover-timeout
配置如下:
sentinel failover-timeout <master-name> <times>
failover-timeout通常被解释成故障转移超时时间,但实际上它作用于故障转移的各个阶段:
a)选出合适从节点。
b)晋升选出的从节点为主节点。
c)命令其余从节点复制新的主节点。
d)等待原主节点恢复后命令它去复制新的主节点。
failover-timeout的作用具体体现在四个方面:
1)如果Redis Sentinel对一个主节点故障转移失败,那么下次再对该主节点做故障转移的起始时间是failover-timeout的2倍。
2)在b)阶段时,如果Sentinel节点向a)阶段选出来的从节点执行slaveof no one一直失败(例如该从节点此时出现故障),当此过程超过failover-timeout时,则故障转移失败。
3)在b)阶段如果执行成功,Sentinel节点还会执行info命令来确认a)阶段选出来的节点确实晋升为主节点,如果此过程执行时间超过failovertimeout时,则故障转移失败。
4)如果c)阶段执行时间超过了failover-timeout(不包含复制时间),则故障转移失败。注意即使超过了这个时间,Sentinel节点也会最终配置从节点去同步最新的主节点。
5. sentinel auth-pass
配置如下:
sentinel auth-pass <master-name> <password>
如果Sentinel监控的主节点配置了密码,sentinel auth-pass配置通过添加主节点的密码,防止Sentinel节点对主节点无法监控。
6. sentinel notification-script
配置如下:
sentinel notification-script <master-name> <script-path>
sentinel notification-script的作用是在故障转移期间,当一些警告级别的Sentinel事件发生(指重要事件,例如-sdown:客观下线、-odown:主观下线)时,会触发对应路径的脚本,并向脚本发送相应的事件参数。
7. sentinel client-reconfig-script
sentinel client-reconfig-script <master-name> <script-path>
sentinel client-reconfig-script的作用是在故障转移结束后,会触发对应路径的脚本,并向脚本发送故障转移结果的相关参数。
9.2.5 如何监控多个主节点
Redis Sentinel可以同时监控多个主节点,具体拓扑图类似于图9-18。配置方法也比较简单,只需要指定多个masterName来区分不同的主节点即可,例如下面的配置监控 monitor master-business-1和 monitor master-business-2 两个主节点:
![](https://img.haomeiwen.com/i18877507/dbaf5ffb2a75729e.png)
9.2.6 调整配置
和普通的Redis数据节点一样,Sentinel节点也支持动态地设置参数,而且和普通的Redis数据节点一样并不是支持所有的参数,具体使用方法如下:
sentinel set <param> <value>
有几点需要注意一下:
- sentinel set命令只对当前Sentinel节点有效。
- sentinel set命令如果执行成功会立即刷新配置文件,这点和Redis普通数据节点设置配置需要执行config rewrite刷新到配置文件不同。
- 建议所有Sentinel节点的配置尽可能一致,这样在故障发现和转移时比较容易达成一致。
- Sentinel对外不支持config命令。
9.2.7 部署技巧
- Sentinel节点不应该部署在一台物理“机器”上。
- 部署至少三个且奇数个的Sentinel节点。
9.3 API
Sentinel节点是一个特殊的Redis节点,它有自己专属的API,本节将对其进行介绍。
- sentinel masters
展示所有被监控的主节点状态以及相关的统计信息 - sentinel master<master name>
展示指定<master name>的主节点状态以及相关的统计信息 - sentinel slaves<master name>
展示指定<master name>的从节点状态以及相关的统计信息 - sentinel sentinels<master name>
展示指定<master name>的Sentinel节点集合(不包含当前Sentinel节点) - sentinel get-master-addr-by-name<master name>
返回指定<master name>主节点的IP地址和端口 - sentinel reset<pattern>
当前Sentinel节点对符合<pattern>(通配符风格)主节点的配置进行重置,包含清除主节点的相关状态(例如故障转移),重新发现从节点和Sentinel节点。 - sentinel failover<master name>
对指定<master name>主节点进行强制故障转移(没有和其他Sentinel节点“协商”),当故障转移完成后,其他Sentinel节点按照故障转移的结果更新自身配置,这个命令在Redis Sentinel的日常运维中非常有用,将在9.6节进行详细介绍。 - sentinel ckquorum<master name>
检测当前可达的Sentinel节点总数是否达到<quorum>的个数。例如quorum=3,而当前可达的Sentinel节点个数为2个,那么将无法进行故障转移,Redis Sentinel的高可用特性也将失去。 - sentinel flushconfig
将Sentinel节点的配置强制刷到磁盘上,这个命令Sentinel节点自身用得比较多,对于开发和运维人员只有当外部原因(例如磁盘损坏)造成配置文件损坏或者丢失时,这个命令是很有用的。 - sentinel remove<master name>
取消当前Sentinel节点对于指定<master name>主节点的监控。 - sentinel monitor <master name> <ip> <port> <quorum>
这个命令和配置文件中的含义是完全一样的,只不过是通过命令的形式来完成Sentinel节点对主节点的监控。 - sentinel set<master name>
动态修改Sentinel节点配置选项,这个命令已经在前面进行了说明,这里就不赘述了。 - sentinel is-master-down-by-addr
Sentinel节点之间用来交换对主节点是否下线的判断,根据参数的不同,还可以作为Sentinel领导者选举的通信方式,具体细节9.5节会介绍。
9.4 客户端连接
虽然我们已经知道了主节点的IP和端口了,但是我们不能这样直接连接过去。因为这样的话,sentinel进行故障转移之后没办法通知到客户端的。所以我们这里的客户端应该想办法连接到sentinel,通过上一节提到的API,获取到主节点的信息。下面介绍,一个Redis Sentinel 客户端的基本实现原理:
实现一个Redis Sentinel客户端的基本步骤如下:
- 遍历Sentinel节点集合获取一个可用的Sentinel节点,后面会介绍Sentinel节点之间可以共享数据,所以从任意一个Sentinel节点获取主节点信息都是可以的。
- 通过sentinel get-master-addr-by-name master-name这个API来获取对应主节点的相关信息
- 验证当前获取的“主节点”是真正的主节点,这样做的目的是为了防止故障转移期间主节点的变化。
- 保持和Sentinel节点集合的“联系”,时刻获取关于主节点的相关“信息”,
从上面的模型可以看出,Redis Sentinel客户端只有在初始化和切换主节点时需要和Sentinel节点集合进行交互来获取主节点信息,所以在设计客户端时需要将Sentinel节点集合考虑成配置(相关节点信息和变化)发现服务。
上述过程只是从客户端设计的角度进行分析,在开发客户端时要考虑的细节还有很多,但是这些问题并不需要深究,下面将介绍如何使用Java的Redis客户端操作Redis Sentinel,并结合本节的内容分析一下相关源码。(不懂java,不做介绍)
9.5 实现原理
本节将介绍Redis Sentinel的基本实现原理,具体包含以下几个方面:Redis Sentinel的三个定时任务、主观下线和客观下线、Sentinel领导者选举、故障转移,相信通过本节的学习读者能对Redis Sentinel的高可用特性有更加深入的理解和认识。
9.5.1 三个定时监控任务
一套合理的监控机制是Sentinel节点判定节点不可达的重要保证,Redis Sentinel通过三个定时监控任务完成对各个节点发现和监控:
- 每隔10秒,每个Sentinel节点会向主节点和从节点发送info命令获取最新的拓扑结构。
- 通过向主节点执行info命令,获取从节点的信息,这也是为什么Sentinel节点不需要显式配置监控从节点。
- 当有新的从节点加入时都可以立刻感知出来。
- 节点不可达或者故障转移后,可以通过info命令实时更新节点拓扑信息。
- 每隔2秒,每个Sentinel节点会向Redis数据节点的sentinel:hello 频道上发送该Sentinel节点对于主节点的判断以及当前Sentinel节点的信息,同时每个Sentinel节点也会订阅该频道,来了解其他Sentinel节点以及它们对主节点的判断,所以这个定时任务可以完成以下两个工作:
- 发现新的Sentinel节点:通过订阅主节点的sentinel:hello了解其他的Sentinel节点信息,如果是新加入的Sentinel节点,将该Sentinel节点信息保存起来,并与该Sentinel节点创建连接。
- Sentinel节点之间交换主节点的状态,作为后面客观下线以及领导者选举的依据.
- 每隔1秒,每个Sentinel节点会向主节点、从节点、其余Sentinel节点发送一条ping命令做一次心跳检测,来确认这些节点当前是否可达。通过上面的定时任务,Sentinel节点对主节点、从节点、其余Sentinel节点都建立起连接,实现了对每个节点的监控,这个定时任务是节点失败判定的重要依据。
9.5.2 主观下线和客观下线
1. 主观下线
上一小节介绍的第三个定时任务,每个Sentinel节点会每隔1秒对主节点、从节点、其他Sentinel节点发送ping命令做心跳检测,当这些节点超过down-after-milliseconds没有进行有效回复,Sentinel节点就会对该节点做失败判定,这个行为叫做主观下线。从字面意思也可以很容易看出主观下线是当前Sentinel节点的一家之言,存在误判的可能。
2. 客观下线
当Sentinel主观下线的节点是主节点时,该Sentinel节点会通过sentinel ismaster-down-by-addr命令向其他Sentinel节点询问对主节点的判断,当超过<quorum>个数,Sentinel节点认为主节点确实有问题,这时该Sentinel节点会做出客观下线的决定,这样客观下线的含义是比较明显了,也就是大部分Sentinel节点都对主节点的下线做了同意的判定,那么这个判定就是客观的。
注意
从节点、Sentinel节点在主观下线后,没有后续的故障转移操作。
9.5.3 领导者Sentinel节点选举
假如Sentinel节点对于主节点已经做了客观下线,那么是不是就可以立即进行故障转移了?当然不是,实际上故障转移的工作只需要一个Sentinel节点来完成即可,所以Sentinel节点之间会做一个领导者选举的工作,选出一个Sentinel节点作为领导者进行故障转移的工作。Redis使用了Raft算法实现领导者选举,因为Raft算法相对比较抽象和复杂,以及篇幅所限,所以这里给出一个Redis Sentinel进行领导者选举的大致思路:
- 每个在线的Sentinel节点都有资格成为领导者,当它确认主节点主观下线时候,会向其他Sentinel节点发送sentinel is-master-down-by-addr命令,要求将自己设置为领导者。
- 收到命令的Sentinel节点,如果没有同意过其他Sentinel节点的sentinel is-master-down-by-addr命令,将同意该请求,否则拒绝。
- 如果该Sentinel节点发现自己的票数已经大于等于max(quorum,num(sentinels)/2+1),那么它将成为领导者。
- 如果此过程没有选举出领导者,将进入下一次选举。
9.5.3 故障转移
领导者选举出的Sentinel节点负责故障转移,具体步骤如下:
- 在从节点列表中选出一个节点作为新的主节点,选择方法如下:
a)过滤:“不健康”(主观下线、断线)、5秒内没有回复过Sentinel节点ping响应、与主节点失联超过down-after-milliseconds*10秒。
b)选择slave-priority(从节点优先级)最高的从节点列表,如果存在则返回,不存在则继续。
c)选择复制偏移量最大的从节点(复制的最完整),如果存在则返回,不存在则继续。
d)选择runid最小的从节点。 - Sentinel领导者节点会对第一步选出来的从节点执行slaveof no one命令让其成为主节点。
- Sentinel领导者节点会向剩余的从节点发送命令,让它们成为新主节点的从节点,复制规则和parallel-syncs参数有关。
- Sentinel节点集合会将原来的主节点更新为从节点,并保持着对其关注,当其恢复后命令它去复制新的主节点。
9.6 开发与运维中的问题
9.6.1 高可用的读写分离
1. 从节点的作用
从节点一般可以起到两个作用:第一,当主节点出现故障时,作为主节点的后备“顶”上来实现故障转移,Redis Sentinel已经实现了该功能的自动化,实现了真正的高可用。第二,扩展主节点的读能力,尤其是在读多写少的场景非常适用,通常的模型如图9-36所示。
![](https://img.haomeiwen.com/i18877507/f409c3cf1b9faba2.png)
但上述模型中,从节点不是高可用的,如果slave-1节点出现故障,首先客户端client-1将与其失联,其次Sentinel节点只会对该节点做主观下线,因为Redis Sentinel的故障转移是针对主节点的。所以很多时候,Redis Sentinel中的从节点仅仅是作为主节点一个热备,不让它参与客户端的读操作,就是为了保证整体高可用性,但实际上这种使用方法还是有一些浪费,尤其是在有很多从节点或者确实需要读写分离的场景,所以如何实现从节点的高可用是非常有必要的。
2. Redis Sentinel读写分离设计思路
Redis Sentinel在对各个节点的监控中,如果有对应事件的发生,都会发出相应的事件消息,其中和从节点变动的事件有以下几个:
- +switch-master:切换主节点(原来的从节点晋升为主节点),说明减少了某个从节点。
- +convert-to-slave:切换从节点(原来的主节点降级为从节点),说明添加了某个从节点。
- +sdown:主观下线,说明可能某个从节点可能不可用(因为对从节点不会做客观下线),所以在实现客户端时可以采用自身策略来实现类似主观下线的功能。
- +reboot:重新启动了某个节点,如果它的角色是slave,那么说明添加了某个从节点。
所以在设计Redis Sentinel的从节点高可用时,只要能够实时掌握所有从节点的状态,把所有从节点看做一个资源池(如图9-37所示),无论是上线还是下线从节点,客户端都能及时感知到(将其从资源池中添加或者删除),这样从节点的高可用目标就达到了。
![](https://img.haomeiwen.com/i18877507/08600383c9247c1b.png)