(六)Redis 哨兵机制

序言

  当主机宕机后出现故障后无法及时恢复,可以在从机执行slave no one命令使其上位变为主机,但是,这样的人工操作带来一个问题,难道半夜主机宕机还要通知运维起来输命令吗?
  这显然不符合常理,为了自动化管理这个过程,Redis 引入了哨兵机制。

简介

  简而言之,哨兵(Sentinel就是一个特殊的 Redis 服务器,其作用如下::

  • 监控:Sentinel 会不断检查您的master和slave 是否按预期工作
  • 故障恢复:若 master 故障, Sentinel 会将一个 slave 提升为master
  • 通知: Sentinel 充当 Redis 客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给连接 Redis 的客户端

搭建

前提条件

  搭建哨兵集群的前提条件是:至少已存在三台 Redis 服务器(一主二从)

  因此,这里我们使用之前已经搭建好的 Redis 主从集群:

主机 redis 路径
192.168.1.8 /data/software/redis/
192.168.1.12 /data/software/redis/
192.168.1.13 /data/software/redis/

机器规划

主机 redis 路径
192.168.1.8 /data/software/redis_sentinel/
192.168.1.12 /data/software/redis_sentinel/
192.168.1.13 /data/software/redis_sentinel/
1
2
3
4
# 目录准备
mkdir -p /data/software/redis_sentinel/{conf,data,logs}
# 配置文件准备
cp ~/redis-6.2.6/sentinel.conf /data/software/redis_sentinel/conf/

配置

  Sentinel 与普通的 Redis 服务器不同(后续详细说明),所以 Redis 安装包默认提供了一份的名为sentinel.conf的哨兵专用配置文件,只有按此配置格式启动的 Redis 才是 Sentinel Redis。

  那么,我们修改下sentinel.conf部分参数,方便后续测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 端口号 默认 26379
port 26379
# 是否守护线程
daemonize yes

logfile "/data/software/redis_sentinel/logs/sentinel.log"

dir /data/software/redis_sentinel/data

sentinel monitor mymaster 192.168.1.8 6379 2
# 主机主观下线时间 默认 30000
#sentinel down-after-milliseconds mymaster 30000
#sentinel parallel-syncs mymaster 1
#sentinel failover-timeout mymaster 180000

  sentinel monitor mymaster 192.168.1.8 6379 2代表哨兵监控名为mymaster的主机,主机 IP 和端口分别为192.168.1.8、6379 ,仲裁数为 2 。
  什么是仲裁呢?
  仲裁,即代表着当主机挂掉后:

  • 若两个 Sentinels 同时认为主机无法访问,则其中一个将尝试进行故障转移工作
  • 若至少总共有三个 Sentinel 可达,则故障转移将被授权并实际开始

启动

1
redis-sentinel /data/software/redis_sentinel/conf/sentinel.conf

  查看启动日志:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
tail -2000f /data/software/redis_sentinel/logs/sentinel.log
8133:X 26 Dec 2022 10:14:09.649 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
8133:X 26 Dec 2022 10:14:09.649 # Redis version=6.2.6, bits=64, commit=00000000, modified=0, pid=8133, just started
8133:X 26 Dec 2022 10:14:09.649 # Configuration loaded
8133:X 26 Dec 2022 10:14:09.649 * Increased maximum number of open files to 10032 (it was originally set to 1024).
8133:X 26 Dec 2022 10:14:09.649 * monotonic clock: POSIX clock_gettime
8133:X 26 Dec 2022 10:14:09.650 * Running mode=sentinel, port=26379.
8133:X 26 Dec 2022 10:14:09.651 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
8133:X 26 Dec 2022 10:14:09.652 # Sentinel ID is 141f2aa615865439533c20a9913ff9a05a22f0f2
8133:X 26 Dec 2022 10:14:09.652 # +monitor master mymaster 192.168.1.8 6379 quorum 2
8133:X 26 Dec 2022 10:14:09.653 * +slave slave 192.168.1.13:6379 192.168.1.13 6379 @ mymaster 192.168.1.8 6379
8133:X 26 Dec 2022 10:14:09.655 * +slave slave 192.168.1.12:6379 192.168.1.12 6379 @ mymaster 192.168.1.8 6379
8133:X 26 Dec 2022 10:14:11.695 * +sentinel sentinel 99e476a7d920b68a90a71a371f3f988117ac669c 192.168.1.8 26379 @ mymaster 192.168.1.8 6379
8133:X 26 Dec 2022 10:14:11.717 * +sentinel sentinel f52ada626dfc89843fca0f5c53f78528645be586 192.168.1.13 26379 @ mymaster 192.168.1.8 6379

  很显然,哨兵监控了主机192.168.1.8 6379,并且知道了其从机信息。

测试

  当主机宕机后(模拟主机关闭):

1
2
# 192.168.1.8 6379
127.0.0.1:6379> shutdown

  过了一段时间后,哨兵日志有如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
8193:X 26 Dec 2022 10:17:23.389 # +sdown master mymaster 192.168.1.8 6379
8193:X 26 Dec 2022 10:17:23.444 # +odown master mymaster 192.168.1.8 6379 #quorum 2/2
8193:X 26 Dec 2022 10:17:23.445 # +new-epoch 1
8193:X 26 Dec 2022 10:17:23.445 # +try-failover master mymaster 192.168.1.8 6379
8193:X 26 Dec 2022 10:17:23.447 # +vote-for-leader f52ada626dfc89843fca0f5c53f78528645be586 1
8193:X 26 Dec 2022 10:17:23.452 # 99e476a7d920b68a90a71a371f3f988117ac669c voted for f52ada626dfc89843fca0f5c53f78528645be586 1
8193:X 26 Dec 2022 10:17:23.456 # 141f2aa615865439533c20a9913ff9a05a22f0f2 voted for f52ada626dfc89843fca0f5c53f78528645be586 1
8193:X 26 Dec 2022 10:17:23.502 # +elected-leader master mymaster 192.168.1.8 6379
8193:X 26 Dec 2022 10:17:23.502 # +failover-state-select-slave master mymaster 192.168.1.8 6379
8193:X 26 Dec 2022 10:17:23.574 # +selected-slave slave 192.168.1.13:6379 192.168.1.13 6379 @ mymaster 192.168.1.8 6379
8193:X 26 Dec 2022 10:17:23.575 * +failover-state-send-slaveof-noone slave 192.168.1.13:6379 192.168.1.13 6379 @ mymaster 192.168.1.8 6379
8193:X 26 Dec 2022 10:17:23.635 * +failover-state-wait-promotion slave 192.168.1.13:6379 192.168.1.13 6379 @ mymaster 192.168.1.8 6379
8193:X 26 Dec 2022 10:17:24.271 # +promoted-slave slave 192.168.1.13:6379 192.168.1.13 6379 @ mymaster 192.168.1.8 6379
8193:X 26 Dec 2022 10:17:24.271 # +failover-state-reconf-slaves master mymaster 192.168.1.8 6379
8193:X 26 Dec 2022 10:17:24.353 * +slave-reconf-sent slave 192.168.1.12:6379 192.168.1.12 6379 @ mymaster 192.168.1.8 6379
8193:X 26 Dec 2022 10:17:24.530 # -odown master mymaster 192.168.1.8 6379
8193:X 26 Dec 2022 10:17:25.283 * +slave-reconf-inprog slave 192.168.1.12:6379 192.168.1.12 6379 @ mymaster 192.168.1.8 6379
8193:X 26 Dec 2022 10:17:25.283 * +slave-reconf-done slave 192.168.1.12:6379 192.168.1.12 6379 @ mymaster 192.168.1.8 6379
8193:X 26 Dec 2022 10:17:25.354 # +failover-end master mymaster 192.168.1.8 6379
8193:X 26 Dec 2022 10:17:25.355 # +switch-master mymaster 192.168.1.8 6379 192.168.1.13 6379
8193:X 26 Dec 2022 10:17:25.355 * +slave slave 192.168.1.12:6379 192.168.1.12 6379 @ mymaster 192.168.1.13 6379
8193:X 26 Dec 2022 10:17:25.355 * +slave slave 192.168.1.8:6379 192.168.1.8 6379 @ mymaster 192.168.1.13 6379

  可以看到,哨兵将192.168.1.13:6379的从机升级为了主机。

  那么,我们在新主机执行命令查看下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 192.168.1.13:6379
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.1.12,port=6379,state=online,offset=81731,lag=1
master_failover_state:no-failover
master_replid:a09577eec36a7fc35d268d13ee00ccc1d0d1f7de
master_replid2:5cf12500fecbc82d8ad6030720f390648f33d1bf
master_repl_offset:81731
second_repl_offset:58537
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:197
repl_backlog_histlen:81535

  可以发现,192.168.1.13:6379确实从从机变为了主机,而192.168.1.12变为了其从机:

  当旧主机重新上线后,变为192.168.1.13:6379的从机:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 192.168.1.8:6379
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:192.168.1.13
master_port:6379
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_read_repl_offset:107298
slave_repl_offset:107298
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:a09577eec36a7fc35d268d13ee00ccc1d0d1f7de
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:107298
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:105321
repl_backlog_histlen:1978

工作原理

  前面我们搭建了一个哨兵集群,可以对 Redis 集群做故障恢复工作,那么,自然就有一些疑问了:

  • 哨兵集群是如何工作的?
  • 哨兵是怎么知道 Redis 服务器的状态?
  • 哨兵是怎么确定新的主机该从哪个从机脱颖而出?
  • 哨兵是怎么能使宕机重启的主机变成新的主机的从机的呢?

  这些疑问,简单来说:

  • 哨兵属于特殊的 Redis 服务器,因此其工作机制和普通的 Redis 不同
  • 哨兵可以根据监控的主机获取主机的信息和主机下所有从机的相关信息
  • 哨兵会检测主从集群的健康状态信息,若主机下线超过一定时间则在从机中投票进行故障转移工作
  • 哨兵还会继续监视已下线的主机,并在它重新上线时,将它设置为新的主服务器的从服务器

  那么,下面我们详细了解下哨兵的工作机制,

Sentinel 的启动与初始化

  当一个 Sentinel 启动时, 它需要执行以下步骤:

  • 初始化服务器
  • 将普通 Redis 服务器使用的代码替换成 Sentinel 专用代码
  • 初始化 Sentinel 状态结构
  • 根据给定的配置文件,初始化 Sentinel 的监视主服务器列表
  • 创建连向主服务器的网络连接

初始化的 Sentinel

  因为 Sentinel 本质上是一个运行在特殊模式的 Redis 服务器,所以启动的第一步,就是初始化一个普通的 Redis 服务器。

  不过,因为 Sentinel 执行的工作和普通 Redis 服务器执行的工作不同,所以 Sentinel 的初始化过程和普通 Redis 服务器的初始化过程并不完全相同。

  例如, 普通 Redis 服务器在初始化时会通过载入 RDB 文件或者 AOF 文件来还原数据库状态, 但是因为 Sentinel 并不使用数据库, 所以初始化 Sentinel 时就不会载入 RDB 文件或者 AOF 文件。

  下表展示了 Redis 服务器在 Sentinel 模式下运行时,服务器各个主要功能的使用情况。

功能 使用情况
数据库和键值对命令,比如 SET、DEL、FLUSHDB 不使用
事务命令,比如 MULTI 和 WATCH 不使用
脚本命令,比如 EVAL 不使用
RDB 持久化命令,比如 SAVE 和 BGSAVE 不使用
AOF 持久化命令,比如 BGREWRITEAOF 不使用
复制命令,比如 SLAVEOF Sentinel 内部可以使用,但客户端不可以使用
发布与订阅命令,比如 PUBLISH 和 SUBSCRIBE SUBSCRIBE、 PSUBSCRIBE、 UNSUBSCRIBE、 PUNSUBSCRIBE 四个命令在 Sentinel 内部和客户端都可以使用,但 PUBLISH 命令只能在 Seatinel 内部使用
文件事件处理器(负责发送命令请求、处理命令回复) Sentinel 内部使用,但关联的文件事件处理器和普通Redis服务器不同
时间事件处理器(负责执行 serverCron 函数) Sentinel 内部使用,时间事件的处理器仍然是 serverCron 函数,其会调用sentinel.c/sentinelrimer 函数,后者包含了 Sentinel 要执行的所有操作

使用 Sentinel 专用代码

  启动Sentinel的第二步就是将一部分 Redis 服务器使用的代码换成Sentinel的专用代码。

  比如普通服务器的getsetsetnx等等命令不再使用,而是使用它专属的代码处理其他事情,如sentinelsubscribeinfo等等命令负责监控相关的功能。
  本质就是原先某些函数库不再使用,而是使用新的函数库,毕竟旧功能咱用不到,新的功能普通 Redis 服务器原来没有,而Sentinel就加上,如前面图示。

  这也解释了为什么Sentinel为什么不能执行原先Redis服务器里的setget等等命令,因为Sentinel根本就没有载入这些命令。

初始化 Sentinel 状态结构

  在应用了Sentinel的专用代码之后,接下来,服务器会初始化一个叫做哨兵状态(sentinelState)的结构,保存了服务器中所有和Sentinel功能相关的状态。

  sentinelState中的 masters 字典记录了所有被 Sentinel 监视的主服务器的相关信息,其中:

  • 字典的键是被监视主服务器的名字
  • 字典的值则是被监视主服务器对应的sentinel.c/sentinelRedisInstance结构。

  每个sentinelRedisInstance结构(后面简称“实例结构”)代表一个被 Sentinel 监视的 Redis 服务器实例(instance),这个实例可以是主服务器、从服务器,或者另外一个 Sentinel。

创建与主机的网络连接

  初始Sentinel的最后一步是创建与被监视主机的网络连接,Sentinel将成为主机的客户端,它可以向主机发送命令,并从命令回复中回去相关的信息。
  对于每个被Sentinel监视的主机,Sentinel都会创建 2 个与主机的异步网络连接:

  • 命令连接:专门用于向主机发送命令,并接受回复信息
  • 订阅连接:专门用于订阅主机的_sentinel_:hello频道

Sentinel 向主机创建网络连接

获取主/从机信息

  当 Sentinel 向主机创建网络连接完成后,就可以获取主机信息和从机信息了。

获取主机信息

  对主机:Sentinel默认会以每十秒一次的频率,通过命令连接向被监视的主机发送info命令来获取主机的当前信息(包括主机本身的信息,及其属下所有从机的信息)。

Sentinel 向带有两个从机的主机发送 INFO 命令

  举个例子,假设如上图所示,现有一主二从的集群,一个 Sentinel 正在连接主服务器,那么 Sentinel 将持续地向主服务器发送 INFO 命令,并获得类似于以下内容的回复:

1
2
3
4
5
6
7
8
9
10
# Server
...
run_id:7611c59dc3a29aa6fa0609f841bb6a1019008a9c
...
# Replication
role:master
...
slave0:ip=127 0.0.1,port=11111,state=online,offset=43,lag=0 slavel:ip=127.0.0.1,port=22222,state=online,offset=43,lag=0
...
# Other sections

  通过分析主服务器返回的 INFO命令回复,Sentinel 可以获取以下两方面的信息:

  • 一方面是关于主服务器本身的信息,包括run_id字段记录的服务器运行 ID,以及role字段记录的服务器角色;
  • 另一方面是关于主服务器属下所有从服务器的信息,每个从服务器都由一个slave字符串开头的行记录,每行的ip字段记录了从服务器的 IP 地址,而port字段则记录了从服务器的端口号。根据这些IP地址和端口号,Sentinel 无须用户提供从服务器的地址信息,就可以自动发现从服务器。

获取从机信息

  当 Sentinel 发现主服务器有新的从服务器出现时,Sentinel 除了会为这个新的从服务器创建相应的实例结构之外,Sentinel 还会创建连接到从服务器的命令连接和订阅连接。

  举个例子,对于之前的主从集群而言,Sentinel 将对 slave1、slave2 两个从服务器分别创建命令连接和订阅连接,如下图所示:

Sentinel 与各个从服务器建立命令连接和订阅连接

  在创建命令连接之后,Sentinel 在默认情况下,会以每十秒一次的频率通过命令连接向从服务器发送INFO命令,并获得类似于以下内容的回复:

1
2
3
4
5
6
7
8
9
10
11
# Server
run _id:32be0699dd27b410f7c90dada3a6fab17f97899f
# Replication
role:slave
master_ host:127.0.0.1
master_port:6379
master_link status:up
slave_repl_offset:11887
slave_priority:100
# Other sections
...

  根据INFO命令的回复,Sentinel 会提取出以下信息:

  • 从服务器的运行IDrun_id
  • 从服务器的角色role
  • 主服务器的 IP 地址master_host,以及主服务器的端口号master_port
  • 主从服务器的连接状态master_link_status
  • 从服务器的优先级slave_priority
  • 从服务器的复制偏移量slave_repl_offset

  根据这些信息,Sentinel 会对从服务器的实例结构sentinelRedisInstance进行更新。

向主/从机发送信息到频道

  默认情况下,Sentinel会以每两秒一次的频率,通过命令向所有被监控的主机和从机发送以下格式的命令:

1
PUBLISH __sentinel__:hello "<s_ip>, <s_port>,<s_runid>,<s_epoch>,<m_name>,<m_ip>,<m_port>,<m_epoch>"

  这个命令包含的信息包括本身的信息,也包括主机的信息。

  举个例子,一个通过命令向主机发送的信息实例:

1
"127.0.0.1,26379,e955b4c85598ef5b5f055bc7ebfd5e828dbed4fa,0,mymaster,127.0.0.1,6379,0"

  这些信息包含:

  • Sentinel的 IP 地址127.0.0.1,端口号26379,运行 ID e955b4c85598ef5b5f055bc7ebfd5e828dbed4fa,当前配置纪元 0
  • 主机的名字mymaster, IP 地址 127.0.0.1,端口号 6379 ,当前的配置纪元 0

接受主/从机的频道信息

  当Sentinel与一个主机或从机建立起订阅连接之后,Sentinel就会通过订阅连接,向服务器发送以下命令:

1
SUBSCRIBE _sentinel_:hello

  Sentinel_sentinel_:hello频道的订阅会一致持续到Sentinel与服务器的连接断开为止。
  这也就是说,对于每个与Sentinel连接的服务器,Sentinel既通过命令连接向服务器的_sentinel_:hello频道发送信息,又通过订阅连接从机的_sentinel_:hello频道接受信息。

  对于监视同一个服务器的多个Sentinel来说,一个Sentinel发送的信息会被其他Sentinel接收到,这些信息会被其他Sentinel用于更新对发送信息的Sentinel的认知,也会被其他Sentinel用于更新对被监视服务器的认知。

  当一个从频道接收到一条信息时,会对该信息进行分析(提取信息中的 IP、port、run ID 等等信息),并进行下列检查:

  • 若信息中记录的Sentinel run ID和接受信息的Sentinel run ID相同,说明是自己发送的,将丢弃这条信息,不做处理
  • 若两者run ID不同,说明是其他Sentinel发送的,将根据信息中的各个参数,Sentinel将对自身创建的主机的实例结构进行更新

更新 sentinels 字典

  Sentinel为主机创建的实例结构中的sentinels字典除了保存Sentinel本身之外,还保存了所有同样监视这个主机的其他Sentinel资料(sentinels字典的键为Sentinel名字,值为对应的Sentinel的实例结构)
  当一个Sentinel目标Sentinel)接收到其他Sentinel源 Sentinel)发来的信息时,目标Sentinel会从信息分析并提取相关参数(Sentinel相关参数如 IP、端口号、run ID 和配置纪元,被监视主机参数如名字、IP、端口号和配置纪元),对自身的实例结构进行更新

创建连向其他 Sentinel 的命令连接

  当Sentinel通过频道信息发现一个新的Sentinel时,它不仅会为新Sentinelsentinels字典中创建相应的实例结构,还会创建一个连接向新Sentinel的命令连接,而新Sentinel也会创建与这个Sentinel的命令连接,最后监视同一主机的多个Sentinel形成一个相互连接的网络,如下图:

各个Sentinel之间的网络连接

注意哦:Sentinel在连接主机或从机时,会同时创建命令连接和订阅连接,但连接其他Sentinel时,只创建命令连接。因为Sentinel需要通过接受主机或从机发来的频道信息来发现未知的新Sentinel,所以需要建立订阅连接,而相互已知的Sentinel只要使用命令连接来进行通信就足够了。

检测主观/客观下线状态

  在默认情况下,Sentinel会以每秒一次的频率向所有与它创建了命令连接的实例(包括所有主/从机,所有其他Sentinel)发送ping命令,并通过实例返回的有效或无效回复来判断实例是否在线。

主观下线状态

  配置文件中的down-after-milliseconds参数指定了Sentinel判断实例进入主观下线的时间长短,若在该时间内实例连续向Sentinel发送无效回复,则Sentinel会修改这个实例的实例结构,来表示这个实例已经进入主观下线状态。

  举个例子,若配置文件指定的down-after-milliseconds的选项值为50000毫秒,那么当主机连续50000毫秒都返回给Sentinel无效回复时,Sentinel就会将主机实例结构修改并标记为主观下线。

  另外,配置文件的down-after-milliseconds参数也会被Sentinel用来判断主机下所有从机的主观下线限时时间标准。

  哦,对了,因为基本不可能只有一个Sentinel,所以其他Sentinel也会执行上面的判断,只不过判断的down-after-milliseconds时间可能不同,因为配置文件设置的不同,所以可能会有这个Sentinel判断主机50000毫秒就主观下线了,而另一个Sentinel却判断主机是70000秒才主观下线的情况发生。

客观下线状态

  当Sentinel将一个主机判断为主观下线之后,为了确认这个主机是否真的下线了,它会向同样监视这一主机的其他Sentinel进行询问,看它们是否也认为主机已经进入下线状态(可以是主观下线或客观下线)。

  当Sentinel从其他Sentinel那里接收到足够数量(总数为设置的仲裁值)的已下线判断之后,Sentinel就会将服务器判断为客观下线,并对主机执行故障转移操作。

选举领头 Sentinel

  当一个主机被判断为客观下线时,监视这个现象主机的各个Sentinel会进行协商,选举出一个领头Sentinel,并由领头Sentinel对下线主机执行故障转移工作。

  以下是 Redis 选举领头 Sentinel 的规则和方法:

  • 所有在线的 Sentinel 都有被选为领头 Sentinel 的资格
  • 每次进行领头 Sentinel 选举之后,不论选举是否成功,所有 Sentinel 的配置纪元( epoch)的值都会自增一次。配置纪元实际上就是一个计数器。
  • 在一个配置纪元里面,所有 Sentinel 都有一次将某个 Sentinel 设置为局部领头 Sentinel 的机会,并且局部领头一旦设置,在这个配置纪元里面就不能再更改
  • 每个发现主服务器进入客观下线的 Sentinel 都会要求其他 Sentinel 将自己设置为局部领头 Sentinel
  • 当一个 Sentinel(源 Sentine1)向另一个 Sentinel(目标 Sentinel)发送SENTINEL is-master-down-by-addr命令, 并且命令中的runid参数不是符号而是源 Sentinel 的运行ID时,这表示源 Sentinel 要求目标 Sentinel 将前者设置为后者的局部领头 Sentinel
  • Sentinel 设置局部领头 Sentinel 的规则是先到先得:最先向目标 Sentinel 发送设置要求的源 Sentinel 将成为目标 Sentinel 的局部领头 Sentinel, 而之后接收到的所有设置要求都会被目标Sentinel 拒绝
  • 目标 Sentinel 在接收到SENTINEL is-master-down-by-addr命令之后,将向源 Sentinel 返回一条命令回复, 回复中的leader_runid参数和leader_epoch参数分别记录了目标Sentinel 的局部领头 Sentinel 的运行 ID 和配置纪元
  • 源 Sentinel 在接收到目标 Sentinel 返回的命令回复之后,会检查回复中leader_epoch参数的值和自己的配置纪元是否相同,如果相同的话,那么源 Sentinel 继续取出回复中的leader_runid参数,如果leader_runid参数的值和源 Sentinel 的运行 ID 一致,那么表示目标 Sentinel 将源 Sentinel 设置成了局部领头 Sentinel。
  • 如果有某个 Sentinel 被半数以上的 Sentinel 设置成了局部领头 Sentinel,那么这个 Sentinel 成为领头 Sentinel。举个例子,在一个由 10 个 Sentinel 组成的 Sentinel 系统里面,只要有大于等于10/2+1=6个 Sentinel 将某个 Sentinel 设置为局部领头 Sentinel,那么被设置的那个 Sentinel 就会成为领头 Sentinel
  • 因为领头 Sentinel 的产生需要半数以上 Sentinel 的支持,并且每个 Sentinel 在每个配置纪元里面只能设置一 次局部领头 Sentinel,所以在一个配置纪元里面,只会出现-一个领头 Sentinel
  • 如果在给定时限内,没有一个 Sentinel 被选举为领头 Sentinel,那么各个 Sentinel 将在一段时间之后再次进行选举,直到选出领头 Sentinel 为止

故障转移

  在选举出领头Sentinel之后,其将对已下线的主机执行故障转移操作,这包含三个步骤:

  • ① 在已下线的主机下的所有从机基于一定挑选一个将其转换为主机(根据从机权值大小、复制偏移量大小等规则挑选);
  • ② 让已下线主机下的所有从机改为复制新的主机;
  • ③ 将已下线主机设置为新的主机的从机,当这个旧主机上线时就会变成新的主机的从机

参考

  • 黄健宏 Redis 设计与实现 [M]. 机械工业出版社,2014

文章信息

时间 说明
2019-04-15 初稿
2022-12-28 完全重构
0%