编辑
2024-06-13
💌中间键
00
请注意,本文编写于 371 天前,最后修改于 225 天前,其中某些信息可能已经过时。

目录

9.4 客户端连接
9.4.2 Redis Sentinel客户端基本实现原理
9.5 实现原理
9.5.1 三个定时监控任务
9.5.2 主观下线和客观下线
1.主观下线
2.客观下线
9.5.3 领导者Sentinel节点选举
9.5.4 故障转移
9.6 开发与运维中的问题
9.6.1 故障转移日志分析
1.Redis Sentinel拓扑结构
2.开始故障转移测试
3.观察效果
4.故障转移分析
5.原主节点后续处理
6.注意点
9.6.2 节点运维
1.节点下线
2.节点上线
3.节点配置
9.6.3 高可用读写分离
1.从节点的作用
2.Redis Sentinel读写分离设计思路

9.4 客户端连接

9.4.2 Redis Sentinel客户端基本实现原理

实现一个Redis Sentinel客户端的基本步骤如下:

  1. 遍历Sentinel节点集合获取一个可用的Sentinel节点,后面会介绍Sentinel节点之间可以共享数据,所以从任意一个Sentinel节点获取主节点信息都是可以的,如图9-22所示。
  2. 通过sentinel get-master-addr-by-name master-name这个API来获取对应主节点的相关信息,如图9-23所示。
  3. 验证当前获取的“主节点”是真正的主节点,这样做的目的是为了防止故障转移期间主节点的变化,如图9-24所示。

image.png

image.png

image.png

  1. 保持和Sentinel节点集合的“联系”,时刻获取关于主节点的相关“信息”,如图9-25所示。

image.png 从上面的模型可以看出,Redis Sentinel客户端只有在初始化和切换主节点时需要和Sentinel节点集合进行交互来获取主节点信息,所以在设计客户端时需要将Sentinel节点集合考虑成配置(相关节点信息和变化)发现服务。

上述过程只是从客户端设计的角度进行分析,在开发客户端时要考虑的细节还有很多,但是这些问题并不需要深究,

下面将介绍如何使用Java的Redis客户端操作Redis Sentinel,并结合本节的内容分析一下相关源码。

9.5 实现原理

本节将介绍Redis Sentinel的基本实现原理,具体包含以下几个方面:

Redis Sentinel的三个定时任务、主观下线和客观下线、Sentinel领导者选举、故障转移,相信通过本节的学习读者能对Redis Sentinel的高可用特性有更加深入的理解和认识。

9.5.1 三个定时监控任务

一套合理的监控机制是Sentinel节点判定节点不可达的重要保证,RedisSentinel通过三个定时监控任务完成对各个节点发现和监控:

  1. 每隔10秒,每个Sentinel节点会向主节点和从节点发送info命令获取最新的拓扑结构,如图9-26所示。

image.png

例如下面就是在一个主节点上执行info replication的结果片段:

# Replication role:master connected_slaves:2 slave0:ip=127.0.0.1,port=6380,state=online,offset=4917,lag=1 slave1:ip=127.0.0.1,port=6381,state=online,offset=4917,lag=1

Sentinel节点通过对上述结果进行解析就可以找到相应的从节点。这个定时任务的作用具体可以表现在三个方面:

  • 通过向主节点执行info命令,获取从节点的信息,这也是为什么Sentinel节点不需要显式配置监控从节点。
  • 当有新的从节点加入时都可以立刻感知出来。
  • 节点不可达或者故障转移后,可以通过info命令实时更新节点拓扑信息。
  1. 每隔2秒,每个Sentinel节点会向Redis数据节点的__sentinel__:hello频道上发送该Sentinel节点对于主节点的判断以及当前Sentinel节点的信息(如图9-27所示),同时每个Sentinel节点也会订阅该频道,来了解其他Sentinel节点以及它们对主节点的判断,所以这个定时任务可以完成以下两个工作:

    • 发现新的Sentinel节点:通过订阅主节点的__sentinel__:hello了解其他的Sentinel节点信息,如果是新加入的Sentinel节点,将该Sentinel节点信息保存起来,并与该Sentinel节点创建连接。
    • Sentinel节点之间交换主节点的状态,作为后面客观下线以及领导者选举的依据。

Sentinel节点publish的消息格式如下:

<Sentinel节点IP> <Sentinel节点端口> <Sentinel节点runId> <Sentinel节点配置版本> <主节点名字> <主节点Ip> <主节点端口> <主节点配置版本>
  1. 每隔1秒,每个Sentinel节点会向主节点、从节点、其余Sentinel节点发送一条ping命令做一次心跳检测,来确认这些节点当前是否可达。如图9-28所示。通过上面的定时任务,Sentinel节点对主节点、从节点、其余Sentinel节点都建立起连接,实现了对每个节点的监控,这个定时任务是节点失败判定的重要依据。

image.png

image.png

9.5.2 主观下线和客观下线

1.主观下线

上一小节介绍的第三个定时任务,每个Sentinel节点会每隔1秒对主节点、从节点、其他Sentinel节点发送ping命令做心跳检测,当这些节点超过down-after-milliseconds没有进行有效回复,Sentinel节点就会对该节点做失败判定,这个行为叫做主观下线。

从字面意思也可以很容易看出主观下线是当前Sentinel节点的一家之言,存在误判的可能,如图9-29所示。

image.png

2.客观下线

当Sentinel主观下线的节点是主节点时,该Sentinel节点会通过sentinel is￾master-down-by-addr命令向其他Sentinel节点询问对主节点的判断,当超过<quorum>个数,Sentinel节点认为主节点确实有问题,这时该Sentinel节点会做出客观下线的决定,这样客观下线的含义是比较明显了,也就是大部分

Sentinel节点都对主节点的下线做了同意的判定,那么这个判定就是客观的,如图9-30所示。

注意

从节点、Sentinel节点在主观下线后,没有后续的故障转移操作。

这里有必要对sentinel is-master-down-by-addr命令做一个介绍,它的使用方法如下:

sentinel is-master-down-by-addr <ip> <port> <current_epoch> <runid>
  • ip:主节点IP。
  • port:主节点端口。
  • current_epoch:当前配置纪元。
  • runid:此参数有两种类型,不同类型决定了此API作用的不同。

当runid等于“*”时,作用是Sentinel节点直接交换对主节点下线的判定。

当runid等于当前Sentinel节点的runid时,作用是当前Sentinel节点希望目标Sentinel节点同意自己成为领导者的请求,有关Sentinel领导者选举,后面会进行介绍。

image.png

例如sentinel-1节点对主节点做主观下线后,会向其余Sentinel节点(假设sentinel-2和sentinel-3节点)发送该命令:

sentinel is-master-down-by-addr 127.0.0.1 6379 0 *

返回结果包含三个参数,如下所示:

  • down_state:目标Sentinel节点对于主节点的下线判断,1是下线,0是在线。
  • leader_runid:当leader_runid等于“*”时,代表返回结果是用来做主节点是否不可达,当leader_runid等于具体的runid,代表目标节点同意runid成为领导者。
  • leader_epoch:领导者纪元。

9.5.3 领导者Sentinel节点选举

假如Sentinel节点对于主节点已经做了客观下线,那么是不是就可以立即进行故障转移了?

当然不是,实际上故障转移的工作只需要一个Sentinel节点来完成即可,所以Sentinel节点之间会做一个领导者选举的工作,选出一个Sentinel节点作为领导者进行故障转移的工作。

Redis使用了Raft算法实现领导者选举,因为Raft算法相对比较抽象和复杂,以及篇幅所限,所以这里给出一个Redis Sentinel进行领导者选举的大致思路:

  1. 每个在线的Sentinel节点都有资格成为领导者,当它确认主节点主观下线时候,会向其他Sentinel节点发送sentinel is-master-down-by-addr命令,要求将自己设置为领导者。
  2. 收到命令的Sentinel节点,如果没有同意过其他Sentinel节点的sentinel is-master-down-by-addr命令,将同意该请求,否则拒绝。
  3. 如果该Sentinel节点发现自己的票数已经大于等于max(quorum,num(sentinels)/2+1),那么它将成为领导者。
  4. 如果此过程没有选举出领导者,将进入下一次选举。

image.png

  1. s1(sentinel-1)最先完成了客观下线,它会向s2(sentinel-2)和s3(sentinel-3)发送sentinel is-master-down-by-addr命令,s2和s3同意选其为领导者。

  2. s1此时已经拿到2张投票,满足了大于等于max(quorum,num(sentinels)/2+1)=2的条件,所以此时s1成为领导者。

由于每个Sentinel节点只有一票,所以当s2向s1和s3索要投票时,只能获取一票,而s3由于最后完成主观下线,当s3向s1和s2索要投票时一票都得不到,整个过程如图9-32和9-33所示。

实际上Redis Sentinel实现会更简单一些,因为一旦有一个Sentinel节点获得了max(quorum,num(sentinels)/2+1)的票数,其他Sentinel节点再去确认已经没有意义了,因为每个Sentinel节点只有一票,如果读者有兴趣的话,可以修改sentinel.c源码,在Sentinel的执行命令列表中添加monitor命令:

image.png

image.png

struct redisCommand sentinelcmds[] = { {"monitor",monitorCommand,1,"",0,NULL,0,0,0,0,0}, {"ping",pingCommand,1,"",0,NULL,0,0,0,0,0}, {"sentinel",sentinelCommand,-2,"",0,NULL,0,0,0,0,0}, ... }

重新编译部署Redis Sentinel测试环境,在3个Sentinel节点上执行monitor命令:

  1. 可以看到sentinel is-master-down-by-addr命令,此命令的执行过程并没有在Redis的日志中有所体现,monitor监控类似如下命令:
// 因为最后参数是"*",所以此时是Sentinel节点之间交换对主节点的失败判定 [0 127.0.0.1:38440] "SENTINEL" "is-master-down-by-addr" "127.0.0.1" "6379" "0" "*" // 因为最后参数是具体的runid,所以此时代表runid="2f4430bb62c039fb125c5771d7cde2571a7 a5ab4"的节点希望目标Sentinel节点同意自己成为领导者。 [0 127.0.0.1:38440] "SENTINEL" "is-master-down-by-addr" "127.0.0.1" "6379" "1" "2f4430bb62c039fb125c5771d7cde2571a7a5ab4"
  1. 选举的过程非常快,基本上谁先完成客观下线,谁就是领导者。
  2. 一旦Sentinel得到足够的票数,不存在图9-32和图9-33的过程。

注意

有关Raft算法可以参考其GitHub主页https://raft.github.io/。

9.5.4 故障转移

领导者选举出的Sentinel节点负责故障转移,具体步骤如下:

  1. 在从节点列表中选出一个节点作为新的主节点,选择方法如下:
    a. 过滤:“不健康”(主观下线、断线)、5秒内没有回复过Sentinel节点ping响应、与主节点失联超过down-after-milliseconds*10秒。 b. 选择slave-priority(从节点优先级)最高的从节点列表,如果存在则返回,不存在则继续。 c. 选择复制偏移量最大的从节点(复制的最完整),如果存在则返回,不存在则继续。 d. 选择runid最小的从节点。

image.png

  1. Sentinel领导者节点会对第一步选出来的从节点执行slaveof no one命令让其成为主节点。

  2. Sentinel领导者节点会向剩余的从节点发送命令,让它们成为新主节点的从节点,复制规则和parallel-syncs参数有关。

  3. Sentinel节点集合会将原来的主节点更新为从节点,并保持着对其关注,当其恢复后命令它去复制新的主节点。

9.6 开发与运维中的问题

9.6.1 故障转移日志分析

1.Redis Sentinel拓扑结构

本次故障转移的分析直接使用9.2节的拓扑和配置进行说明,为了方便分析故障转移的过程,表9-4列出了每个节点的角色、ip、端口、进程号、runId。

image.png

因为故障转移涉及节点关系的变化,所以下面说明中用端口号代表节点。

2.开始故障转移测试

模拟故障的方法有很多,比较典型的方法有以下几种:

  • 方法一,强制杀掉对应节点的进程号,这样可以模拟出宕机的效果。
  • 方法二,使用Redis的debug sleep命令,让节点进入睡眠状态,这样可以模拟阻塞的效果。
  • 方法三,使用Redis的shutdown命令,模拟正常的停掉Redis。

本次我们使用方法一进行测试,因为从实际经验来看,数百上千台机器偶尔宕机一两台是会不定期出现的,为了方便分析日志行为,这里记录一下操作的时间和命令。

用kill-9使主节点的进程宕机,操作时间2016-07-2409:40:35:

$ kill -9 19661

3.观察效果

6380节点晋升为主节点,6381节点成为6380节点的从节点。

4.故障转移分析

相信故障转移的效果和预想的一样,这里重点分析相应节点的日志。

(1)6379节点日志

两个复制请求,分别来自端口为6380和6381的从节点:

19661:M 24 Jul 09:22:16.907 * Slave 127.0.0.1:6380 asks for synchronization 19661:M 24 Jul 09:22:16.907 * Full resync requested by slave 127.0.0.1:6380 ... 19661:M 24 Jul 09:22:16.919 * Synchronization with slave 127.0.0.1:6380 succeeded 19661:M 24 Jul 09:22:23.396 * Slave 127.0.0.1:6381 asks for synchronization 19661:M 24 Jul 09:22:23.396 * Full resync requested by slave 127.0.0.1:6381 ... 19661:M 24 Jul 09:22:23.432 * Synchronization with slave 127.0.0.1:6381 succeeded

09:40:35做了kill-9操作,由于模拟的是宕机效果,所以6379节点没 有看到任何日志(这点和shutdown操作不太相同)。

(2)6380节点日志

6380节点在09:40:35之后发现它与6379节点已经失联:

19667:S 24 Jul 09:40:35.788 # Connection with master lost. 19667:S 24 Jul 09:40:35.788 * Caching the disconnected master state. 19667:S 24 Jul 09:40:35.974 * Connecting to MASTER 127.0.0.1:6379 19667:S 24 Jul 09:40:35.974 * MASTER <-> SLAVE sync started 19667:S 24 Jul 09:40:35.975 # Error condition on socket for SYNC: Connection refused ...

09:41:06时它接到Sentinel节点的命令:清理原来缓存的主节点状态,Sentinel节点将6380节点晋升为主节点,并重写配置:

19667:M 24 Jul 09:41:06.161 * Discarding previously cached master state. 19667:M 24 Jul 09:41:06.161 * MASTER MODE enabled (user request from 'id=7 addr=127.0.0.1:46759 fd=10 name=sentinel-7044753f-cmd age=1111 idle=0 flags=x db=0 sub=0 psub=0 multi=3 qbuf=0 qbuf-free=32768 obl=36 oll=0 omem=0 events=rw cmd=exec') 19667:M 24 Jul 09:41:06.161 # CONFIG REWRITE executed with success. 6381节点发来了复制请求: 19667:M 24 Jul 09:41:07.499 * Slave 127.0.0.1:6381 asks for synchronization 19667:M 24 Jul 09:41:07.499 * Full resync requested by slave 127.0.0.1:6381 ... 19667:M 24 Jul 09:41:07.548 * Background saving terminated with success 19667:M 24 Jul 09:41:07.548 * Synchronization with slave 127.0.0.1:6381 succeeded

(3)6381节点日志

6381节点同样与6379节点失联:

19685:S 24 Jul 09:40:35.788 # Connection with master lost. 19685:S 24 Jul 09:40:35.788 * Caching the disconnected master state. 19685:S 24 Jul 09:40:36.425 * Connecting to MASTER 127.0.0.1:6379 19685:S 24 Jul 09:40:36.425 * MASTER <-> SLAVE sync started 19685:S 24 Jul 09:40:36.425 # Error condition on socket for SYNC: Connection refused ...

后续操作如下:

  1. 09:41:06时它接到Sentinel节点的命令,清理原来缓存的主节点状态,让它去复制新的主节点(6380节点):
    19685:S 24 Jul 09:41:06.497 # Error condition on socket for SYNC: Connection refused 19685:S 24 Jul 09:41:07.008 * Discarding previously cached master state. 19685:S 24 Jul 09:41:07.008 * SLAVE OF 127.0.0.1:6380 enabled (user request from 'id=7 addr=127.0.0.1:55872 fd=10 name=sentinel-7044753f-cmd age=1111 idle=0 flags=x db=0 sub=0 psub=0 multi=3 qbuf=133 qbuf-free=32635 obl=36 oll=0 omem=0 events=rw cmd=exec') 19685:S 24 Jul 09:41:07.008 # CONFIG REWRITE executed with success.
  2. 向新的主节点(6380节点)发起复制操作:
    19685:S 24 Jul 09:41:07.498 * Connecting to MASTER 127.0.0.1:6380 ... 19685:S 24 Jul 09:41:07.549 * MASTER <-> SLAVE sync: Finished with success

(4)sentinel-1节点日志

09:41:05对6379节点作了主观下线(+sdown),注意这个时间正好是kill-9后的30秒,和down-after-milliseconds的配置是一致的。

Sentinel节点更新自己的配置纪元(new-epoch):

19697:X 24 Jul 09:41:05.850 # +sdown master mymaster 127.0.0.1 6379 19697:X 24 Jul 09:41:05.928 # +new-epoch 1

后续操作如下:

  1. 投票给sentinel-3节点:
19697:X 24 Jul 09:41:05.929 # +vote-for-leader 7044753f564e42b1578341acf4c49dca 3681151c 1 19697:X 24 Jul 09:41:06.913 # +odown master mymaster 127.0.0.1 6379 #quorum 3/2
  1. 更新状态:从sentinel-3节点(领导者)得知:故障转移后6380节点变为主节点,并发现了两个从节点6381和6379,并在30秒后对(09:41:07~09:41:37)6379节点做了主观下线:
19697:X 24 Jul 09:41:06.913 # Next failover delay: I will not start a failover before Sun Jul 24 09:47:06 2016 19697:X 24 Jul 09:41:07.008 # +config-update-from sentinel 127.0.0.1:26381 127.0.0.1 26381 @ mymaster 127.0.0.1 6379 19697:X 24 Jul 09:41:07.008 # +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6380 19697:X 24 Jul 09:41:07.008 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6380 19697:X 24 Jul 09:41:07.008 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380 19697:X 24 Jul 09:41:37.060 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380

(5)sentinel-2节点日志

整个过程和sentinel-1节点是一样的,这里就不占用篇幅分析了。

(6)sentinel-3节点日志

从sentinel-1节点和sentinel-2节点的日志来看,sentinel-3节点是领导者,所以分析sentinel-3节点的日志至关重要。后续操作如下。

  1. 达到了客观下线的条件:
19713:X 24 Jul 09:41:05.854 # +sdown master mymaster 127.0.0.1 6379 19713:X 24 Jul 09:41:05.909 # +odown master mymaster 127.0.0.1 6379 #quorum 2/2 19713:X 24 Jul 09:41:05.909 # +new-epoch 1
  1. sentinel-3节点被选为领导者:
19713:X 24 Jul 09:41:05.909 # +try-failover master mymaster 127.0.0.1 6379 19713:X 24 Jul 09:41:05.911 # +vote-for-leader 7044753f564e42b1578341acf4c49dca 3681151c 1 19713:X 24 Jul 09:41:05.929 # 127.0.0.1:26379 voted for 7044753f564e42b1578341a cf4c49dca3681151c 1 19713:X 24 Jul 09:41:05.930 # 127.0.0.1:26380 voted for 7044753f564e42b1578341a 547 cf4c49dca3681151c 1 19713:X 24 Jul 09:41:06.001 # +elected-leader master mymaster 127.0.0.1 6379

表9-5展示了3个Sentinel节点完成客观下线的时间点,从时间点可以看到sentinel-3节点最先完成客观下线。

image.png 3) 故障转移。每一步都可以通过发布订阅来获取,对于每个字段的说明可以参考表9-6。寻找合适的从节点作为新的主节点:

19713:X 24 Jul 09:41:06.001 # +failover-state-select-slave master mymaster 127.0.0.1 6379

选出了合适的从节点(6380节点):

19713:X 24 Jul 09:41:06.077 # +selected-slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379

命令6380节点执行slaveof no one,使其成为主节点:

19713:X 24 Jul 09:41:06.077 * +failover-state-send-slaveof-noone slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379

等待6380节点晋升为主节点:

19713:X 24 Jul 09:41:06.161 * +failover-state-wait-promotion slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379

确认6380节点已经晋升为主节点:

19713:X 24 Jul 09:41:06.927 # +promoted-slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379

故障转移进入重新配置从节点阶段:

19713:X 24 Jul 09:41:06.927 # +failover-state-reconf-slaves master mymaster 127.0.0.1 6379

命令6381节点复制新的主节点:

19713:X 24 Jul 09:41:07.008 * +slave-reconf-sent slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379

6381节点正在重新配置成为6380节点的从节点,但是同步过程尚未完成:

19713:X 24 Jul 09:41:07.955 * +slave-reconf-inprog slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379

6381节点完成对6380节点的同步:

19713:X 24 Jul 09:41:07.955 * +slave-reconf-done slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379

故障转移顺利完成:

19713:X 24 Jul 09:41:08.045 # +failover-end master mymaster 127.0.0.1 6379

故障转移成功后,发布主节点的切换消息:

19713:X 24 Jul 09:41:08.045 # +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6380

表9-6记录了Redis Sentinel在故障转移一些重要的事件消息对应的频道。

image.png

<instance details>格式如下: <instance-type> <name> <ip> <port> @ <master-name> <master-ip> <master-port>

5.原主节点后续处理

重新启动原来的6379节点:

redis-server redis-6379.conf操作时间: 2016-07-24 09:46:21

(1)6379节点

启动后接到Sentinel节点的命令,让它去复制6380节点:

22223:M 24 Jul 09:46:21.260 * The server is now ready to accept connections on port 6379 22223:S 24 Jul 09:46:31.323 * SLAVE OF 127.0.0.1:6380 enabled (user request from 'id=2 addr=127.0.0.1:51187 fd=6 name=sentinel-94dde2f5-cmd age=10 idle=0 flags=x db=0 sub=0 psub=0 multi=3 qbuf=0 qbuf-free=32768 obl=36 oll=0 omem=0 events=rw cmd=exec') 22223:S 24 Jul 09:46:31.323 # CONFIG REWRITE executed with success. ...

(2)6380节点

接到6379节点的复制请求,做复制的相应处理:

19667:M 24 Jul 09:46:32.284 * Slave 127.0.0.1:6379 asks for synchronization 19667:M 24 Jul 09:46:32.284 * Full resync requested by slave 127.0.0.1:6379 ... 19667:M 24 Jul 09:46:32.353 * Synchronization with slave 127.0.0.1:6379 succeeded

(3)sentinel-1节点日志

撤销对6379节点主观下线的决定:

19707:X 24 Jul 09:46:21.406 # -sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380

(4)sentinel-2节点日志

撤销对6379节点主观下线的决定:

19713:X 24 Jul 09:46:21.408 # -sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380

(5)sentinel-3节点日志

撤销对6379节点主观下线的决定,更新Sentinel节点配置:

19697:X 24 Jul 09:46:21.367 # -sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380 19697:X 24 Jul 09:46:31.322 * +convert-to-slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380

6.注意点

部署各个节点的机器时间尽量要同步,否则日志的时序性会混乱,例如可以给机器添加NTP服务来同步时间,具体可以参考第12章Linux配置章节。

9.6.2 节点运维

1.节点下线

在介绍如何进行节点下线之前,首先需要弄清两个概念:临时下线和永久下线。

  • 临时下线:暂时将节点关掉,之后还会重新启动,继续提供服务。
  • 永久下线:将节点关掉后不再使用,需要做一些清理工作,如删除配置文件、持久化文件、日志文件。

所以运维人员需要弄清楚本次下线操作是临时下线还是永久下线。

通常来看,无论是主节点、从节点还是Sentinel节点,下线原因无外乎以下几种:

  • 节点所在的机器出现了不稳定或者即将过保被回收。
  • 节点所在的机器性能比较差或者内存比较小,无法支撑应用方的需求。
  • 节点自身出现服务不正常情况,需要快速处理。

(1)主节点

如果需要对主节点进行下线,比较合理的做法是选出一个“合适”(例如性能更高的机器)的从节点,使用sentinel failover功能将从节点晋升主节点,sentinel failover已经在9.3节介绍过了,只需要在任意可用的Sentinel节点

执行如下操作即可。

sentinel failover <master name>

如图9-35所示,在任意一个Sentinel节点上(例如26379端口节点)执行sentinel failover即可。

运维提示

Redis Sentinel存在多个从节点时,如果想将指定从节点晋升为主节点,

可以将其他从节点的slavepriority配置为0,但是需要注意failover后,将 slave-priority调回原值。

image.png

(2)从节点和Sentinel节点

如果需要对从节点或者Sentinel节点进行下线,只需要确定好是临时还是永久下线后执行相应操作即可。

如果使用了读写分离,下线从节点需要保证应用方可以感知从节点的下线变化,从而把读取请求路由到其他节点。

需要注意的是,Sentinel节点依然会对这些下线节点进行定期监控,这是由Redis Sentinel的设计思路所决定的。

下面日志显示(需要设置loglevel=debug),6380节点下线后,Sentinel节点还是会定期对其监控,会 造成一定的网络资源浪费。

-cmd-link slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379 #Connection refused -pubsub-link slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379 #Connection refused ...

2.节点上线

(1)添加从节点

添加从节点的场景大致有如下几种:

  • 使用了读写分离,但现有的从节点无法支撑应用方的流量。
  • 主节点没有可用的从节点,无法支持故障转移。
  • 添加一个更强悍的从节点利用手动failover替换主节点。

添加方法:添加slaveof{masterIp}{masterPort}的配置,使用redis-server启动即可,它将被Sentinel节点自动发现。

(2)添加Sentinel节点

添加Sentinel节点的场景可以分为以下几种:

  • 当前Sentinel节点数量不够,无法达到Redis Sentinel健壮性要求或者无法达到票数。
  • 原Sentinel节点所在机器需要下线。

添加方法:添加sentinel monitor主节点的配置,使用redis-sentinel启动即可,它将被其余Sentinel节点自动发现。

(3)添加主节点

因为Redis Sentinel中只能有一个主节点,所以不需要添加主节点,如果需要替换主节点,可以使用Sentinel failover手动故障转移。

3.节点配置

有关Redis数据节点和Sentinel节点配置修改以及优化的方法,前面的章节已经介绍过了,这里给出Sentinel节点配置时要注意的地方:

  • Sentinel节点配置尽可能一致,这样在判断节点故障时会更加准确。
  • Sentinel节点支持的命令非常有限,例如config命令是不支持的,而Sentinel节点也需要dir、loglevel之类的配置,所以尽量在一开始规划好,不过所幸Sentinel节点不存储数据,如果需要修改配置,重新启动即可。

运维提示

Sentinel节点只支持如下命令:ping、sentinel、subscribe、unsubscribe、 psubscribe、punsubscribe、publish、info、role、client、shutdown。 具体可以参考源码中sentinel.c。

上面介绍了Redis Sentinel节点运维的场景和方法,但在实际运维中,故障的发生通常比较突然并且瞬息万变,影响的范围也很难预估,

所以建议运维人员将上述场景提前做好预案,当事故发生时,可以用脚本或者可视化工具快速处理故障。

9.6.3 高可用读写分离

1.从节点的作用

从节点一般可以起到两个作用:

  • 第一,当主节点出现故障时,作为主节点的后备“顶”上来实现故障转移,Redis Sentinel已经实现了该功能的自动化,实现了真正的高可用。
  • 第二,扩展主节点的读能力,尤其是在读多写少的场景非常适用,通常的模型如图9-36所示。

image.png

但上述模型中,从节点不是高可用的,如果slave-1节点出现故障,首先客户端client-1将与其失联,其次Sentinel节点只会对该节点做主观下线,因为Redis Sentinel的故障转移是针对主节点的。

所以很多时候,Redis Sentinel中的从节点仅仅是作为主节点一个热备,不让它参与客户端的读操作,就是为了保证整体高可用性,但实际上这种使用方法还是有一些浪费,尤其是在有很多从节点或者确实需要读写分离的场景,所以如何实现从节点的高可用是非常有必要的。

2.Redis Sentinel读写分离设计思路

Redis Sentinel在对各个节点的监控中,如果有对应事件的发生,都会发出相应的事件消息(见表9-6),其中和从节点变动的事件有以下几个:

  • +switch-master:切换主节点(原来的从节点晋升为主节点),说明减少了某个从节点。
  • +convert-to-slave:切换从节点(原来的主节点降级为从节点),说明添加了某个从节点。
  • +sdown:主观下线,说明可能某个从节点可能不可用(因为对从节点不会做客观下线),所以在实现客户端时可以采用自身策略来实现类似主观下线的功能。
  • +reboot:重新启动了某个节点,如果它的角色是slave,那么说明添加了某个从节点。

所以在设计Redis Sentinel的从节点高可用时,只要能够实时掌握所有从节点的状态,把所有从节点看做一个资源池(如图9-37所示),无论是上线还是下线从节点,客户端都能及时感知到(将其从资源池中添加或者删除),这样从节点的高可用目标就达到了。

image.png

本文作者:Eric

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!