原文:High availability with Redis Sentinel
基于非redis-cluster的高可用方案
Redis Sentinel为非Redis-cluster模式下的Redis提供高可用。
Redis Sentinel还提供其他附加任务,如监控、通知,以及作为客户端的配置提供者。
以下是Sentinel功能的全面列表(宏观层面):
- 监控 Sentinel不断检查您的主节点和副本实例是否按预期工作
- 通知 如果监控的Redis实例中有问题,Sentinel可以通过API通知系统管理员或其他计算机程序
- 自动故障转移 如果主节点工作不正常,Sentinel可以启动故障转移过程,其中一个副本被提升为主节点,其他额外的副本被重新配置以使用新的主节点,使用Redis服务器的应用程序被通知新地址以便连接
- 配置提供者 Sentinel充当客户端服务发现的权威来源:客户端连接到Sentinels以询问负责给定服务的当前Redis主节点的地址。如果发生故障转移,Sentinels将报告新地址
Sentinel 作为一个分布式系统
Redis Sentinel 是一个分布式系统:
Sentinel 本身设计为多个 Sentinel 进程一起协作的配置中运行。拥有多个协作的 Sentinel 进程的优势如下:
- 当多个 Sentinel 达成一致,认为某个给定的主节点不再可用时,才执行故障检测。这降低了误报的概率。
- 即使不是所有的 Sentinel 进程都在工作,Sentinel 也能正常工作,使系统对故障具有强大的抵抗力。毕竟,拥有一个本身就是单点故障的故障转移系统是没有意义的。 Sentinels、Redis 实例(主节点和副本)以及连接到 Sentinel 和 Redis 的客户端,一起也构成了一个具有特定属性的更大的分布式系统。本文档将逐步介绍概念,从理解 Sentinel 的基本属性所需的基本信息开始,到更复杂的信息(这些是可选的),以便理解 Sentinel 的确切工作方式。
Sentinel 快速入门
获取 Sentinel
当前版本的 Sentinel 被称为 Sentinel 2
。它是对最初 Sentinel 实现的重写,采用了更强大、更简单易预测的算法(这些算法在本文档中进行了解释)。
自 Redis 2.8 版本起,Redis Sentinel 提供了稳定版本。
新的开发工作在不稳定分支上进行,一旦新功能被认为是稳定的,有时会被回溯到最新的稳定分支中。
随 Redis 2.6 版本提供的 Redis Sentinel 版本 1 已被弃用,不应再使用。
运行 Sentinel
如果您使用的是 redis-sentinel
可执行文件(或者您有一个指向 redis-server
可执行文件的符号链接,命名为 redis-sentinel
),您可以使用以下命令行运行 Sentinel:
redis-sentinel /path/to/sentinel.conf
或者,您也可以直接使用 redis-server
可执行文件,以 Sentinel 模式启动它:
redis-server /path/to/sentinel.conf --sentinel
这两种方式的工作原理相同。
然而,运行 Sentinel 时使用配置文件是强制性
的,因为系统将使用此文件来保存当前状态,以便在重启时重新加载。如果没有给出配置文件,或者配置文件路径不可写,Sentinel 将拒绝启动。
Sentinel 默认监听 TCP 端口 26379
,因此要使 Sentinel 工作,您的服务器的 26379
端口必须开放,以接收来自其他 Sentinel 实例的 IP 地址的连接。否则,Sentinel 无法通信,也无法就该做什么达成一致,因此永远不会执行故障转移。
部署 Sentinel 前需要知道的基本事项
- 为了稳健的部署,您需要至少三个 Sentinel 实例
- 这三个 Sentinel 实例应该放置在被认为以独立方式失败的计算机或虚拟机中。例如,不同的物理服务器或在不同可用性区域上执行的虚拟机
- Sentinel + Redis 分布式系统不保证在故障期间保留已确认的写操作,因为 Redis 使用异步复制。然而,有些部署 Sentinel 的方式可以使丢失写操作的窗口限制在特定时刻,而其他部署方式则安全性较低
- 您需要在客户端中支持 Sentinel。流行的客户端库支持 Sentinel,但并非所有库都支持
- 如果您不时在开发环境中测试,或者如果可能的话,在生产环境中测试,没有任何 HA(高可用性)设置是安全的。您可能有一个配置错误,只有在太晚的时候(比如凌晨3点,当您的主服务器停止工作时)才会显现
- Sentinel、Docker 或其他形式的网络地址转换或端口映射应谨慎混合使用:Docker 执行端口重新映射,破坏了 Sentinel 自动发现其他 Sentinel 进程和主服务器的副本列表的能力。更多信息请检查本文档后面关于 Sentinel 和 Docker 的部分
配置 Sentinel
Redis 源代码分发包含一个名为 sentinel.conf
的文件,这是一个自文档化的示例配置文件,您可以使用它来配置 Sentinel。然而,一个典型的最小配置文件看起来如下:
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1
sentinel monitor resque 192.168.1.3 6380 4
sentinel down-after-milliseconds resque 10000
sentinel failover-timeout resque 180000
sentinel parallel-syncs resque 5
您只需要指定要监视的主服务器,给每个分开的主服务器(可能有任意数量的副本)一个不同的名称。无需指定副本,副本会被自动发现。Sentinel 将自动更新配置,添加关于副本的额外信息(以便在重启的情况下保留信息)。在故障转移期间每次将副本提升为主服务器,以及每次发现新的 Sentinel 时,配置也会被重写。
上面的示例配置基本上监视了两组 Redis 实例,每组由一个主服务器和未定义数量的副本组成。一组实例称为 mymaster,另一组称为 resque。
sentinel monitor
语句的参数含义如下:
sentinel monitor <master-name> <ip> <port> <quorum>
为了清晰起见,让我们逐行检查配置选项的含义:
第一行用于告诉 Redis 监视一个名为 mymaster 的主服务器,该服务器位于地址 127.0.0.1 和端口 6379,quorum
为2。除了quorum
参数外,其他都是很明显的:
quorum
是指需要同意主服务器不可达的 Sentinel 数量,以便真正将主服务器标记为失败,并在可能的情况下最终启动故障转移程序- 然而,
quorum
仅用于检测失败。为了实际执行故障转移,需要选举一个 Sentinel 作为故障转移的领导者并被授权继续进行。这只会在大多数 Sentinel 进程的投票下发生
所以,例如,如果您有 5 个 Sentinel 进程,并且给定主服务器的quorum
设置为 2,则会:
- 如果两个 Sentinel 同时同意主服务器不可达,其中一个将尝试开始故障转移
- 如果至少有三个 Sentinel 总共可达,故障转移将被授权并实际开始
实际上,这意味着在失败期间,如果大多数 Sentinel 进程无法通话(即在少数分区中没有故障转移),Sentinel 永远不会启动故障转移。
其他 Sentinel 选项
其他选项几乎总是以下形式:
sentinel <option_name> <master_name> <option_value>
它们用于以下目的:
-
down-after-milliseconds
是一个实例在 Sentinel 开始认为它已经宕机之前应当不可达的时间(以毫秒为单位),不可达的原因可以是它没有回应我们的 PING,或者它回应了一个错误。 -
parallel-syncs
设置了在故障转移后可以同时被重新配置以使用新主服务器的副本数量。这个数字越小,故障转移过程完成所需的时间就越长,然而,如果副本被配置为提供旧数据,您可能不希望所有副本同时与主服务器重新同步。尽管复制过程对于副本来说大部分时间是非阻塞的,但在它停下来从主服务器加载批量数据的那一刻,您可能希望通过将此选项设置为 1 来确保一次只有一个副本不可达。
其他选项在本文档的其余部分有描述,并在 Redis 发行版附带的示例 sentinel.conf
文件中有文档记录。
配置参数可以在运行时修改:
- 针对特定主服务器的配置参数使用
SENTINEL SET
进行修改。 - 全局配置参数使用
SENTINEL CONFIG SET
进行修改。
有关详细信息,请参阅运行时重新配置 Sentinel 部分。
示例 Sentinel 部署
现在您知道了有关 Sentinel 的基本信息,您可能想知道应该将 Sentinel 进程放在哪里,需要多少个 Sentinel 进程等等。本节展示了一些示例部署。
我们使用 ASCII 艺术来以图形格式展示配置示例,这是不同符号的含义:
+--------------------+
| This is a computer |
| or VM that fails |
| independently. We |
| call it a "box" |
+--------------------+
我们在box里写上它们运行的内容:
+-------------------+
| Redis master M1 |
| Redis Sentinel S1 |
+-------------------+
不同的box通过线连接,以显示它们能够通话:
+-------------+ +-------------+
| Sentinel S1 |---------------| Sentinel S2 |
+-------------+ +-------------+
网络分区显示为使用斜杠的中断线:
+-------------+ +-------------+
| Sentinel S1 |------ // ------| Sentinel S2 |
+-------------+ +-------------+
还请注意:
- 主服务器被称为 M1, M2, M3, ..., Mn。
- 副本被称为 R1, R2, R3, ..., Rn(R 代表副本)。
- Sentinel 被称为 S1, S2, S3, ..., Sn。
- 客户端被称为 C1, C2, C3, ..., Cn。
- 当实例因为 Sentinel 的操作而改变角色时,我们将其放在方括号内,所以 [M1] 意味着现在因为 Sentinel 的干预而成为主服务器的实例。
请注意,我们永远不会展示只使用两个 Sentinel
的设置,因为 Sentinel 总是需要与大多数进行通信才能开始故障转移。
示例 1:只有两个 Sentinel,不要这么做
+----+ +----+
| M1 |---------| R1 |
| S1 | | S2 |
+----+ +----+
配置:quorum = 1
在这个设置中,如果主服务器 M1 失败,R1 将被提升,因为两个 Sentinel 可以就故障达成一致(显然,仲裁设置为 1),并且也可以授权故障转移,因为多数为二。所以表面上看似乎可以工作,然而请检查下一个要点以了解为什么这个设置是有问题的。
如果运行 M1 的box停止工作,S1 也会停止工作。在另一个box中运行的 Sentinel S2 将无法授权故障转移,所以系统将变得不可用。
请注意,需要多数才能命令不同的故障转移,并且稍后将最新配置传播给所有 Sentinel。还请注意,在上述设置的一个侧面中,没有任何协议就能进行故障转移,这将是非常危险的:
+----+ +------+
| M1 |----//-----| [M1] |
| S1 | | S2 |
+----+ +------+
在上述配置中,我们以完全对称的方式创建了两个主服务器(假设 S2 可以在未经授权的情况下进行故障转移)。客户端可以无限期地向两边写入数据,当分区愈合时,没有办法理解哪个配置是正确的,以防止永久的脑裂状况。
所以请始终至少在三个不同的box中部署三个 Sentinel。
示例 2:三个box的基本设置
这是一个非常简单的设置,具有简单调整以增加安全性的优势。它基于三个box,每个box都运行一个 Redis 进程和一个 Sentinel 进程。
+----+
| M1 |
| S1 |
+----+
|
+----+ | +----+
| R2 |----+----| R3 |
| S2 | | S3 |
+----+ +----+
配置:quorum = 2
如果主服务器 M1 失败,S2 和 S3 将就故障达成一致,并且能够授权故障转移,使客户端能够继续。
在每个 Sentinel 设置中,由于 Redis 使用异步复制,总是存在丢失一些写入的风险,因为给定的已确认写入可能无法到达被提升为主服务器的副本。然而,在上述设置中,由于客户端与旧主服务器分区隔离,风险更高,如下图所示:
+----+
| M1 |
| S1 | <- C1(写入将丢失)
+----+
|
/
/
+------+ | +----+
| [M2] |----+----| R3 |
| S2 | | S3 |
+------+ +----+
在这种情况下,网络分区隔离了旧主服务器 M1,因此副本 R2 被提升为主服务器。然而,像 C1 这样与旧主服务器同一分区的客户端可能会继续向旧主服务器写入数据。这些数据将永远丢失,因为当分区愈合时,主服务器将被重新配置为新主服务器的副本,丢弃其数据集。
使用以下 Redis 复制功能可以缓解这个问题,该功能允许在主服务器检测到它不再能够将其写入传输给指定数量的副本时停止接受写入。
min-replicas-to-write 1
min-replicas-max-lag 10
通过上述配置(请参阅 Redis 发行版中的自注释 redis.conf 示例以获取更多信息),当作为主服务器时,如果 Redis 实例不能至少写入 1 个副本,它将停止接受写入。由于复制是异步的,无法写入实际上意味着副本要么断开连接,要么在超过指定的最大延迟秒数后不向我们发送异步确认。
使用这种配置,上例中的旧 Redis 主服务器 M1 将在 10 秒后变得不可用。当分区愈合时,Sentinel 配置将收敛到新配置,客户端 C1 将能够获取有效配置并继续使用新主服务器。
然而,没有免费的午餐。通过这种改进,如果两个副本都宕机,主服务器将停止接受写入。这是一个折衷。
示例 3:Sentinel 放在客户端box中
有时我们只有两个 Redis box可用,一个用于主服务器,一个用于副本。在这种情况下,示例 2 中的配置是不可行的,所以我们可以采用以下配置,将 Sentinel 放在客户端所在的位置:
+----+ +----+
| M1 |----+----| R1 |
| | | | |
+----+ | +----+
|
+------------+------------+
| | |
| | |
+----+ +----+ +----+
| C1 | | C2 | | C3 |
| S1 | | S2 | | S3 |
+----+ +----+ +----+
配置:quorum = 2
在这个设置中,Sentinel 的观点与客户端相同:如果主服务器可以被大多数客户端访问,那么它就是好的。C1、C2、C3 在这里是通用客户端,并不意味着 C1 标识连接到 Redis 的单个客户端。更有可能是像应用服务器、Rails 应用之类的东西。
如果运行 M1 和 S1 的box失败,故障转移将无问题地发生,然而,可以看出不同的网络分区将导致不同的行为。例如,如果客户端与 Redis 服务器之间的网络断开,Sentinel 将无法设置,因为 Redis 主服务器和副本都将不可用。
请注意,如果 C3 与 M1 分区(在上述网络中几乎不可能,但在不同的布局中或因为软件层面的故障更可能发生),我们将遇到类似于示例 2 中描述的问题,不同之处在于这里我们没有办法打破对称性,因为只有一个副本和主服务器,所以主服务器在与其副本断开连接时不能停止接受查询,否则在副本故障期间主服务器将永远不可用。
所以这是一个有效的设置,但示例 2 中的设置具有优势,例如 Redis 的 HA 系统运行在与 Redis 本身相同的box中,这可能更简单地管理,并且能够限制少数分区中的主服务器接收写入的时间量。
示例 4:客户端边少于三个客户端的 Sentinel 配置
在示例 3 描述的设置中,如果客户端边(例如三个网站服务器)少于三个box,就不能使用。在这种情况下,我们需要采用以下混合设置:
+----+ +----+
| M1 |----+----| R1 |
| S1 | | | S2 |
+----+ | +----+
|
+------+-----+
| |
| |
+----+ +----+
| C1 | | C2 |
| S3 | | S4 |
+----+ +----+
配置:quorum = 3
这与示例 3 中的设置相似,但在这里我们在四个可用的box中运行四个 Sentinel。如果主服务器 M1 变得不可用,其他三个 Sentinel 将执行故障转移。
理论上,通过移除运行 C2 和 S4 的box,并将仲裁设置为 2,这个设置仍然可以工作。然而,如果我们的应用层没有高可用性,我们不太可能希望在 Redis 侧实现 HA。
Sentinel、Docker、NAT 及可能的问题
Docker 使用一种称为端口映射的技术:在 Docker 容器内运行的程序可能会使用与程序认为正在使用的不同的端口来暴露。这在同时在同一服务器上运行使用相同端口的多个容器时非常有用。
Docker 并不是唯一发生这种情况的软件系统,还有其他网络地址转换 (NAT) 设置,其中端口可能被重新映射,有时不仅是端口,还有 IP 地址。
端口和地址的重新映射以两种方式给 Sentinel 带来问题:
- Sentinel 自动发现其他 Sentinel 不再工作,因为它基于 hello 消息,每个 Sentinel 在其中宣布它们正在监听连接的端口和 IP 地址。然而,Sentinel 无法理解地址或端口被重新映射,因此它宣布的信息对其他 Sentinel 来说是不正确的。
- 在 Redis 主服务器的 INFO 输出中以类似方式列出的副本:地址是通过检查 TCP 连接的远程对端而被主服务器检测到的,而端口是在握手期间由副本本身宣布的,然而,由于在点 1 中所述的原因,端口可能是错误的。
由于 Sentinel 使用主服务器的 INFO 输出信息自动检测副本,检测到的副本将无法访问,Sentinel 将永远无法对主服务器进行故障转移,因为从系统的角度来看,没有好的副本,所以目前没有办法使用 Sentinel 监控一组使用 Docker 部署的主服务器和副本实例,除非你指示 Docker 进行 1:1 的端口映射。
对于第一个问题,如果你想使用 Docker 运行一组 Sentinel 实例,并使用转发端口(或任何其他端口被重新映射的 NAT 设置),你可以使用以下两个 Sentinel 配置指令来强制 Sentinel 宣布特定的 IP 和端口集:
sentinel announce-ip <ip>
sentinel announce-port <port>
请注意,Docker 有能力运行在主机网络模式下(查看 --net=host
选项获取更多信息)。这应该不会产生问题,因为在这种设置中端口没有被重新映射。
IP 地址和 DNS 名称
旧版本的 Sentinel 不支持主机名,要求到处指定 IP 地址。从 6.2 版本开始,Sentinel 开始可选地支持主机名。
这个能力默认是禁用的。如果你打算启用 DNS/主机名支持,请注意:
- 你的 Redis 和 Sentinel 节点上的名称解析配置必须是可靠的,并且能够快速解析地址。地址解析中的意外延迟可能对 Sentinel 产生负面影响。
- 你应该到处使用主机名,避免混合使用主机名和 IP 地址。为此,请分别为所有 Redis 和 Sentinel 实例使用
replica-announce-ip <hostname>
和sentinel announce-ip <hostname>
。
启用 resolve-hostnames
全局配置允许 Sentinel 接受主机名:
- 作为 sentinel monitor 命令的一部分
- 作为副本地址,如果副本使用主机名值进行
replica-announce-ip
Sentinel 将接受主机名作为有效输入并解析它们,但在宣布实例、更新配置文件等时仍然会引用 IP 地址。
启用 announce-hostnames
全局配置使 Sentinel 使用主机名。这影响对客户端的回复、写入配置文件的值、发给副本的 REPLICAOF 命令等。
这种行为可能不与所有 Sentinel 客户端兼容,它们可能明确期望一个 IP 地址。
当客户端使用 TLS 连接到实例并且需要一个名称而不是 IP 地址以执行证书 ASN 匹配时,使用主机名可能是有用的。
快速教程
在本文档的后续部分,将逐步介绍关于 Sentinel API、配置和语义的所有细节。然而,对于那些想要尽快与系统互动的人来说,这一节是一个教程,展示了如何配置和与三个 Sentinel 实例进行交互。
这里我们假设实例在端口 5000、5001、5002 上执行。我们还假设你有一个在端口 6379 上运行的 Redis 主服务器,以及一个在端口 6380 上运行的副本。在本教程中,我们将在所有地方使用 IPv4 回环地址 127.0.0.1,假设你在个人电脑上运行模拟。
三个 Sentinel 配置文件应该如下所示:
port 5000
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1
其他两个配置文件将相同,但使用 5001 和 5002 作为端口号。
关于上述配置有几点需要注意:
- 主服务器集被称为 mymaster。它标识了主服务器及其副本。由于每个主服务器集都有不同的名称,Sentinel 可以同时监控不同的主服务器和副本集。
quorum
设置为 2(sentinel monitor 配置指令的最后一个参数)。- down-after-milliseconds 的值是 5000 毫秒,即 5 秒,所以如果我们在这段时间内没有收到我们的 ping 的任何回复,主服务器将被检测为失败。
一旦你启动了三个 Sentinel,你将看到它们记录的一些消息,比如:
+monitor master mymaster 127.0.0.1 6379 quorum 2
这是一个 Sentinel 事件,如果你订阅了事件名称,你可以通过 Pub/Sub 接收到这类事件,具体将在 Pub/Sub 消息部分后面说明。
Sentinel 在故障检测和故障转移期间生成并记录不同的事件。
询问 Sentinel 关于主服务器的状态
与 Sentinel 开始的最明显的事情是检查它监控的主服务器是否运行良好:
$ redis-cli -p 5000
127.0.0.1:5000> sentinel master mymaster
1) "name"
2) "mymaster"
3) "ip"
4) "127.0.0.1"
5) "port"
6) "6379"
7) "runid"
8) "953ae6a589449c13ddefaee3538d356d287f509b"
9) "flags"
10) "master"
11) "link-pending-commands"
12) "0"
13) "link-refcount"
14) "1"
15) "last-ping-sent"
16) "0"
17) "last-ok-ping-reply"
18) "735"
19) "last-ping-reply"
20) "735"
21) "down-after-milliseconds"
22) "5000"
23) "info-refresh"
24) "126"
25) "role-reported"
26) "master"
27) "role-reported-time"
28) "532439"
29) "config-epoch"
30) "1"
31) "num-slaves"
32) "1"
33) "num-other-sentinels"
34) "2"
35) "quorum"
36) "2"
37) "failover-timeout"
38) "60000"
39) "parallel-syncs"
40) "1"
如你所见,它打印了关于主服务器的许多信息。对我们来说有几个特别感兴趣的点:
num-other-sentinels
是 2,所以我们知道 Sentinel 已经检测到这个主服务器有另外两个 Sentinelflags
仅仅是 master。如果主服务器宕机,我们也可以在这里看到s_down
或o_down
标志num-slaves
正确设置为 1,所以 Sentinel 也检测到我们的主服务器有一个连接的副本
为了更多地探索这个实例,你可能需要以下两个命令:
SENTINEL replicas mymaster
SENTINEL sentinels mymaster
第一个命令将提供关于连接到主服务器的副本的类似信息,第二个命令提供关于其他 Sentinel 的信息。
获取当前主服务器的地址
正如我们已经指定的,Sentinel 还充当客户端想要连接到一组主服务器和副本的配置提供者。由于可能的故障转移或重新配置,客户端不知道给定一组实例的当前活动主服务器是谁,所以 Sentinel 导出了一个 API 来询问这个问题:
127.0.0.1:5000> SENTINEL get-master-addr-by-name mymaster
1) "127.0.0.1"
2) "6379"
测试故障转移
此时,我们的 Sentinel 部署已经准备好进行测试。我们可以简单地杀死我们的主服务器并检查配置是否更改。为此,我们可以做如下操作:
redis-cli -p 6379 DEBUG sleep 30
这个命令将使我们的主服务器不再可达,睡眠 30 秒。它基本上模拟
了主服务器因某种原因挂起
。
如果检查 Sentinel 日志,应该能看到很多动作:
- 每个 Sentinel 用
+sdown
事件检测到主服务器宕机 - 这个事件后来升级为
+odown
,意味着多个 Sentinel 同意主服务器不可达 - Sentinel 投票给一个 Sentinel 将开始第一次故障转移尝试
- 故障转移发生
如果再次询问 mymaster 的当前主服务器地址,我们应该会得到不同的回复:
127.0.0.1:5000> SENTINEL get-master-addr-by-name mymaster
1) "127.0.0.1"
2) "6380"
到目前为止一切顺利... 此时你可以跳到创建你的 Sentinel 部署,或者继续阅读以了解所有 Sentinel 命令和内部机制。
Sentinel API
Sentinel 提供了一个 API 来检查其状态,检查被监控的主服务器和副本的健康状况,订阅以接收特定通知,并在运行时更改 Sentinel 配置。
默认情况下,Sentinel 使用 TCP 端口 26379 运行(注意 6379 是 Redis 的正常端口)。Sentinel 接受使用 Redis 协议的命令,因此你可以使用 redis-cli 或任何其他未修改的 Redis 客户端来与 Sentinel 通信。
可以直接查询 Sentinel 以检查从其视角看被监控的 Redis 实例的状态,看看它知道哪些其他 Sentinel 等。另外,使用 Pub/Sub,每当某些事件发生时,例如故障转移或实例进入错误条件等,可以从 Sentinel 接收推送式通知。
Sentinel 命令
SENTINEL
命令是 Sentinel 的主要 API。以下是其子命令列表(适用的最小版本已注明):
SENTINEL CONFIG GET <name> (>= 6.2)
获取全局 Sentinel 配置参数的当前值。指定的名称可以是通配符,类似于 RedisCONFIG GET
命令SENTINEL CONFIG SET <name> <value> (>= 6.2)
设置全局 Sentinel 配置参数的值SENTINEL CKQUORUM <master name>
检查当前 Sentinel 配置是否能够达到进行主服务器故障转移所需的仲裁数,以及授权故障转移所需的多数。这个命令应该在监控系统中使用,以检查 Sentinel 部署是否正常SENTINEL FLUSHCONFIG
强制 Sentinel 将其配置重写到磁盘上,包括当前的 Sentinel 状态。通常 Sentinel 每次其状态发生变化时(在持久化到磁盘的状态子集的上下文中)都会重写配置。然而,有时可能由于操作错误、磁盘故障、包升级脚本或配置管理器,配置文件丢失。在这些情况下,强制 Sentinel 重写配置文件的方法很方便。即使先前的配置文件完全丢失,此命令也能工作SENTINEL FAILOVER <master name>
强制进行故障转移,就好像主服务器无法访问一样,并且不请求其他 Sentinel 的同意(然而,新版本的配置将被发布,以便其他 Sentinel 更新他们的配置)SENTINEL GET-MASTER-ADDR-BY-NAME <master name>
返回该名称的主服务器的 IP 和端口号。如果这个主服务器的故障转移正在进行中或已成功完成,它返回被提升为主服务器的副本的地址和端口SENTINEL INFO-CACHE (>= 3.2)
返回主服务器和副本的缓存 INFO 输出SENTINEL IS-MASTER-DOWN-BY-ADDR
检查从当前 Sentinel 的视角看,由 ip:port 指定的主服务器是否宕机。这个命令主要用于内部使用SENTINEL MASTER <master name>
显示指定主服务器的状态和信息SENTINEL MASTERS
显示被监控的主服务器及其状态的列表SENTINEL MONITOR
开始 Sentinel 的监控。有关更多信息,请参阅运行时重新配置 Sentinel 部分SENTINEL MYID (>= 6.2)
返回 Sentinel 实例的 IDSENTINEL PENDING-SCRIPTS
此命令返回有关待处理脚本的信息SENTINEL REMOVE
停止 Sentinel 的监控。有关更多信息,请参阅运行时重新配置 Sentinel 部分SENTINEL REPLICAS <master name> (>= 5.0)
显示此主服务器的副本列表及其状态SENTINEL SENTINELS <master name>
显示此主服务器的 Sentinel 实例列表及其状态SENTINEL SET
设置 Sentinel 的监控配置。有关更多信息,请参阅运行时重新配置 Sentinel 部分SENTINEL SIMULATE-FAILURE (crash-after-election|crash-after-promotion|help) (>= 3.2)
此命令模拟不同的 Sentinel 崩溃场景SENTINEL RESET <pattern>
此命令将重置所有匹配名称的主服务器。模式参数是一个 glob 风格的模式。重置过程清除主服务器中的任何先前状态(包括正在进行的故障转移),并移除已经发现并与主服务器关联的每个副本和 Sentinel
为了连接管理和管理目的,Sentinel 支持以下 Redis 命令的子集:
ACL (>= 6.2)
此命令管理 Sentinel 访问控制列表。有关更多信息,请参阅 ACL 文档页面和 Sentinel 访问控制列表认证部分AUTH (>= 5.0.1)
认证客户端连接。有关更多信息,请参阅 AUTH 命令和配置 Sentinel 实例认证部分CLIENT
此命令管理客户端连接。有关更多信息,请参阅其子命令的页面COMMAND (>= 6.2)
此命令返回关于命令的信息。有关更多信息,请参阅 COMMAND 命令及其各种子命令HELLO (>= 6.0)
切换连接的协议。有关更多信息,请参阅 HELLO 命令INFO
返回有关 Sentinel 服务器的信息和统计数据。有关更多信息,请参阅 INFO 命令PING
此命令简单返回 PONGROLE
此命令返回字符串 "sentinel" 和一个被监控主服务器的列表。有关更多信息,请参阅 ROLE 命令SHUTDOWN
关闭 Sentinel 实例
最后,Sentinel 还支持 SUBSCRIBE
、UNSUBSCRIBE
、PSUBSCRIBE
和 PUNSUBSCRIBE
命令。有关更多详情,请参阅 Pub/Sub 消息部分。
在运行时重新配置 Sentinel
从 Redis 2.8.4 版本开始,Sentinel 提供了一个 API,以便添加、移除或更改给定主服务器的配置。请注意,如果您有多个 Sentinel,您应该将更改应用于所有实例,以便 Redis Sentinel 能够正确工作。这意味着更改单个 Sentinel 的配置不会自动将更改传播到网络中的其他 Sentinel。
以下是用于更新 Sentinel 实例配置的 SENTINEL 子命令列表:
SENTINEL MONITOR <name> <ip> <port> <quorum>
此命令告诉 Sentinel 开始监控具有指定名称、ip、端口和仲裁数的新主服务器。它与 sentinel.conf 配置文件中的 sentinel monitor 配置指令相同,不同之处在于您不能使用主机名作为 ip,而需要提供 IPv4 或 IPv6 地址SENTINEL REMOVE <name>
用于移除指定的主服务器:主服务器将不再被监控,并且将完全从 Sentinel 的内部状态中移除,因此它将不再被 SENTINEL masters 等列出SENTINEL SET <name> [<option> <value> ...]
SET 命令与 Redis 的 CONFIG SET 命令非常相似,用于更改特定主服务器的配置参数。可以指定多个选项/值对(或根本不指定)。所有可以通过 sentinel.conf 配置的参数也可以使用 SET 命令进行配置
以下是一个使用 SENTINEL SET 命令修改名为 objects-cache 的主服务器的 down-after-milliseconds 配置的示例:
SENTINEL SET objects-cache-master down-after-milliseconds 1000
如前所述,SENTINEL SET 可用于设置在启动配置文件中可设置的所有配置参数。此外,可以不移除并重新添加主服务器就更改仅主服务器仲裁配置,只需使用:
SENTINEL SET objects-cache-master quorum 5
请注意,没有相应的 GET 命令,因为 SENTINEL MASTER 以易于解析的格式(作为字段/值对数组)提供了所有配置参数。
从 Redis 6.2 版本开始,Sentinel 还允许获取和设置之前仅在配置文件中支持的全局配置参数:
SENTINEL CONFIG GET <name>
获取全局 Sentinel 配置参数的当前值。指定的名称可以是通配符,类似于 Redis CONFIG GET 命令SENTINEL CONFIG SET <name> <value>
设置全局 Sentinel 配置参数的值
可以操作的全局参数包括:
resolve-hostnames
,announce-hostnames
。参见 IP 地址和 DNS 名称announce-ip
,announce-port
。参见 Sentinel、Docker、NAT 和可能的问题sentinel-user
,sentinel-pass
。参见配置带认证的 Sentinel 实例
添加或移除 Sentinel
由于 Sentinel 实现了自动发现机制,因此添加新的 Sentinel 到您的部署中是一个简单的过程。您需要做的就是启动配置为监控当前活动主服务器的新 Sentinel。在 10 秒内,Sentinel 将获取其他 Sentinel 的列表以及附加到主服务器的副本集。
如果您需要一次添加多个 Sentinel,建议一个接一个地添加,等待所有其他 Sentinel 已经知道第一个之后再添加下一个。这在添加新 Sentinel 的过程中仍然有助于保证只有一方可以达到多数,以防在添加新 Sentinel 的过程中发生故障。
这可以通过在添加每个新 Sentinel 时延迟 30 秒并在没有网络分区的情况下轻松实现。
在过程结束时,可以使用命令 SENTINEL MASTER mastername
来检查所有 Sentinel 是否一致同意监控主服务器的 Sentinel 总数。
移除一个 Sentinel 稍微复杂一些:Sentinel 永远不会忘记已经看到的 Sentinel,即使它们很长时间不可达,因为我们不想动态改变授权故障转移和创建新配置编号所需的多数。因此,为了移除一个 Sentinel,应该在没有网络分区的情况下执行以下步骤:
- 停止您想要移除的 Sentinel 进程
- 向所有其他 Sentinel 实例发送
SENTINEL RESET *
命令(* 可以用确切的主服务器名称替换,如果您只想重置单个主服务器)。一个接一个,每个实例之间至少等待 30 秒 - 通过检查每个 Sentinel 的 SENTINEL MASTER mastername 的输出,检查所有 Sentinel 是否一致同意当前活动的 Sentinel 数量
移除旧主服务器或无法访问的副本
Sentinel 永远不会忘记给定主服务器的副本,即使它们很长时间无法访问。这是有用的,因为 Sentinel 应该能够在网络分区或故障事件后正确重新配置返回的副本。
此外,在故障转移后,失败的主服务器虚拟地作为新主服务器的副本添加,这样它将在再次可用时被重新配置以复制新的主服务器。
然而,有时您可能想永远从 Sentinel 监控的副本列表中移除一个副本(可能是旧主服务器)。
为了做到这一点,您需要向所有 Sentinel 发送 SENTINEL RESET mastername 命令:他们将在接下来的 10 秒内刷新副本列表,只添加从当前主服务器 INFO 输出正确复制的副本。
Pub/Sub 消息
客户端可以将 Sentinel 作为兼容 Redis 的 Pub/Sub 服务器使用(但不能使用 PUBLISH 命令),以便 SUBSCRIBE 或 PSUBSCRIBE 到频道并获得有关特定事件的通知。
频道名称与事件名称相同。例如,名为 +sdown 的频道将接收与实例进入 SDOWN 状态(SDOWN 意味着该实例从查询的 Sentinel 的视角看已不再可达)相关的所有通知。
要获取所有消息,只需使用 PSUBSCRIBE * 订阅即可。
以下是您可以使用此 API 接收的频道和消息格式列表。第一个词是频道/事件名称,其余的是数据的格式。
注意:在指定实例详情时,意味着提供以下参数来标识目标实例:
<instance-type> <name> <ip> <port> @ <master-name> <master-ip> <master-port>
标识主服务器的部分(从 @ 参数到结束)是可选的,仅在实例本身不是主服务器时指定。
+reset-master <instance details>
-- 主服务器被重置+slave <instance details>
-- 检测到新副本并连接+failover-state-reconf-slaves <instance details>
-- 故障转移状态更改为 reconf-slaves 状态+failover-detected <instance details>
-- 检测到由另一个 Sentinel 或任何其他外部实体启动的故障转移(一个连接的副本变成了主服务器)+slave-reconf-sent <instance details>
-- 领导 Sentinel 向该实例发送 REPLICAOF 命令,以便为新副本重新配置+slave-reconf-inprog <instance details>
-- 正在重新配置的副本显示为新主服务器 ip:port 对的副本,但同步过程尚未完成+slave-reconf-done <instance details>
-- 副本现在与新主服务器同步-dup-sentinel <instance details>
-- 作为重复的一个或多个指定主服务器的 Sentinel 被移除(例如,当 Sentinel 实例重启时会发生这种情况)+sentinel <instance details>
-- 检测到此主服务器的新 Sentinel 并连接+sdown <instance details>
-- 指定实例现在处于主观下线状态-sdown <instance details>
-- 指定实例不再处于主观下线状态+odown <instance details>
-- 指定实例现在处于客观下线状态-odown <instance details>
-- 指定实例不再处于客观下线状态+new-epoch <instance details>
-- 当前纪元已更新+try-failover <instance details>
-- 新故障转移正在进行中,等待多数选举+elected-leader <instance details>
-- 赢得了指定纪元的选举,可以执行故障转移+failover-state-select-slave <instance details>
-- 新故障转移状态是 select-slave:我们正在尝试找到合适的副本进行晋升no-good-slave <instance details>
-- 没有合适的副本可以晋升,目前我们会在一段时间后再尝试,但这可能会改变,如果出现这种情况,状态机最终会完全中止故障转移selected-slave <instance details>
-- 我们找到了指定的合适副本进行晋升failover-state-send-slaveof-noone <instance details>
-- 我们正在尝试将晋升的副本重新配置为主服务器,等待它切换failover-end-for-timeout <instance details>
-- 故障转移因超时终止,副本最终将配置为与新主服务器同步failover-end <instance details>
-- 故障转移成功终止,所有副本似乎都重新配置为与新主服务器同步switch-master <master name> <oldip> <oldport> <newip> <newport>
-- 在配置更改后,主服务器的新 IP 和地址是指定的+tilt
-- 进入倾斜模式-tilt
-- 退出倾斜模式
处理 -BUSY 状态
当 Lua 脚本运行时间超过配置的 Lua 脚本时间限制时,Redis 实例会返回-BUSY
错误,当这种情况在触发故障转移之前发生时,Redis Sentinel 会尝试发送 SCRIPT KILL 命令,只有当脚本是只读的时候,这个命令才会成功
如果实例在这次尝试之后仍然处于错误状态,最终将执行故障转移
副本优先级
Redis 实例有一个称为 replica-priority 的配置参数。这个信息由 Redis 副本实例在其 INFO 输出中公开,并且 Sentinel 在选择用于主服务器故障转移的副本时会使用这个信息:
- 如果副本优先级设置为 0,该副本永远不会被提升为主服务器
- Sentinel 更倾向于选择具有较低优先级数字的副本
例如,如果有一个副本 S1 位于当前主服务器的同一个数据中心,另一个副本 S2 位于另一个数据中心,可以将 S1 的优先级设置为 10,将 S2 的优先级设置为 100,这样如果主服务器出现故障且 S1 和 S2 都可用时,S1 将被优先选择
有关副本选择方式的更多信息,请查看本文档的副本选择和优先级部分。
Sentinel 和 Redis 认证
当主服务器配置为要求客户端认证时,作为安全措施,副本也需要知道凭据,以便与主服务器认证并创建用于异步复制协议的主-副本连接。
Redis 访问控制列表认证
从 Redis 6 开始,用户认证和权限通过访问控制列表(ACL)管理。
为了使 Sentinel 在配置了 ACL 的情况下连接到 Redis 服务器实例,Sentinel 配置必须包括以下指令:
sentinel auth-user <master-name> <username>
sentinel auth-pass <master-name> <password>
其中 <username>
和 <password>
是用于访问该组实例的用户名和密码。这些凭据应该在所有组的 Redis 实例上配置,且具有最小控制权限。例如:
127.0.0.1:6379> ACL SETUSER sentinel-user ON >somepassword allchannels +multi +slaveof +ping +exec +subscribe +config|rewrite +role +publish +info +client|setname +client|kill +script|kill
Redis 仅密码认证
直到 Redis 6,认证通过以下配置指令实现:
requirepass
在主服务器中设置,以设置认证密码,并确保实例不会处理未经认证的客户端的请求masterauth
在副本中设置,以便副本能够与主服务器认证并正确地从中复制数据
当使用 Sentinel 时,并没有单一的主服务器,因为在故障转移后,副本可能扮演主服务器的角色,而旧的主服务器可以被重新配置以充当副本,所以你需要在所有实例中都设置上述指令,无论是主服务器还是副本。
这通常也是一个明智的设置,因为你不想只在主服务器中保护数据,同时在副本中也能访问相同的数据。
然而,在不常见的情况下,如果你需要一个无需认证即可访问的副本,你仍然可以通过设置副本优先级为零来实现,以防止这个副本被提升为主服务器,并且仅在这个副本中配置 masterauth
指令,不使用 requirepass
指令,这样数据将可以被未认证的客户端读取。
为了使 Sentinel 在配置了 requirepass
的情况下连接到 Redis 服务器实例,Sentinel 配置必须包括 sentinel auth-pass
指令,格式为:
sentinel auth-pass <master-name> <password>
配置 Sentinel 实例以进行身份验证
Sentinel 实例本身可以通过要求客户端使用 AUTH 命令进行认证来保证安全。从 Redis 6.2 开始,可以使用访问控制列表(ACL),而在之前的版本(从 Redis 5.0.1 开始)支持仅密码认证。
请注意,Sentinel 的认证配置应应用于部署中的每个实例,并且所有实例都应使用相同的配置。此外,ACL 和仅密码认证不应同时使用。
Sentinel 访问控制列表认证
使用 ACL 保护 Sentinel 实例的第一步是防止任何未经授权的访问。为此,您需要禁用默认的超级用户(或至少使用强密码设置它),并创建一个新的超级用户并允许它访问 Pub/Sub 频道:
127.0.0.1:5000> ACL SETUSER admin ON >admin-password allchannels +@all
OK
127.0.0.1:5000> ACL SETUSER default off
OK
默认用户被 Sentinel 用来连接其他实例。您可以使用以下配置指令提供另一个超级用户的凭据:
sentinel sentinel-user <username>
sentinel sentinel-pass <password>
其中 <username>
和 <password>
分别是 Sentinel 的超级用户和密码(例如上面示例中的 admin 和 admin-password)。
最后,对于认证传入的客户端连接,您可以创建如下的 Sentinel 受限用户配置文件:
127.0.0.1:5000> ACL SETUSER sentinel-user ON >user-password -@all +auth +client|getname +client|id +client|setname +command +hello +ping +role +sentinel|get-master-addr-by-name +sentinel|master +sentinel|myid +sentinel|replicas +sentinel|sentinels +sentinel|masters
有关更多信息,请参考您选择的 Sentinel 客户端文档。
Sentinel 仅密码认证
要使用仅密码认证的 Sentinel,在所有 Sentinel 实例中添加 requirepass 配置指令,如下所示:
requirepass "your_password_here"
这样配置后,Sentinels 将做两件事:
- 客户端需要密码才能向 Sentinels 发送命令。这很明显,因为这就是这类配置指令在 Redis 中的一般工作方式
- 此外,配置用于访问本地 Sentinel 的密码,将被该 Sentinel 实例用于认证它连接到的所有其他 Sentinel 实例
这意味着您将不得不在所有 Sentinel 实例中配置相同的 requirepass 密码。这样,每个 Sentinel 都可以与其他 Sentinel 通信,无需为每个 Sentinel 配置访问所有其他 Sentinel 的密码,这将非常不切实际。
在使用此配置之前,请确保您的客户端库可以向 Sentinel 实例发送 AUTH 命令。
Sentinel 客户端实现
除非系统配置为执行脚本,该脚本执行将所有请求透明地重定向到新的主实例(虚拟 IP 或其他类似系统),否则 Sentinel 需要显式的客户端支持。关于客户端库实现的主题在文档 Sentinel 客户端指南中有所涵盖。
更高级的概念
在以下部分,我们将介绍一些关于 Sentinel 工作方式的细节,不涉及将在本文档最后部分介绍的实现细节和算法。
SDOWN 和 ODOWN 故障状态
Redis Sentinel 有两种不同的故障概念,一种称为主观下线条件(SDOWN),是特定 Sentinel 实例本地的故障条件。另一种称为客观下线条件(ODOWN),当足够多的 Sentinel(至少与被监控主服务器的 quorum 参数配置的数量一致)具有 SDOWN 条件,并通过 SENTINEL is-master-down-by-addr 命令从其他 Sentinel 获取反馈时,就会达到这个状态。
从 Sentinel 的角度来看,当它在配置中设置的 is-master-down-after-milliseconds 参数指定的秒数内没有收到对 PING 请求的有效回复时,就会达到 SDOWN 条件。
对 PING 的可接受回复包括以下几种:
- PING 收到 +PONG 回复。
- PING 收到 -LOADING 错误。
- PING 收到 -MASTERDOWN 错误。
任何其他回复(或根本没有回复)都被认为是无效的。然而,请注意,在 INFO 输出中将自己标记为副本的逻辑主服务器被视为已下线。
请注意,SDOWN 要求在整个配置的间隔内都没有收到可接受的回复,因此例如,如果间隔是 30000 毫秒(30 秒),我们每 29 秒收到一个可接受的 ping 回复,实例被认为是工作的。
SDOWN 不足以触发故障转移:它只意味着单个 Sentinel 认为 Redis 实例不可用。要触发故障转移,必须达到 ODOWN 状态。
从 SDOWN 切换到 ODOWN 时,并没有使用严格的共识算法,而是使用了一种形式的流言:如果一个给定的 Sentinel 在给定的时间范围内得到足够多 Sentinel 的报告,称主服务器不工作,SDOWN 就会被提升为 ODOWN。如果这种确认后来缺失了,标志就会被清除。
要真正开始故障转移需要更严格的授权,这需要实际的多数同意,但在没有达到 ODOWN 状态的情况下,不会触发任何故障转移。
ODOWN 条件仅适用于主服务器。对于其他类型的实例,Sentinel 不需要采取行动,因此副本和其他 Sentinel 永远不会达到 ODOWN 状态,只有 SDOWN。
然而,SDOWN 也有语义上的含义。例如,处于 SDOWN 状态的副本不会被执行故障转移的 Sentinel 选为晋升。
Sentinel 和副本自动发现
Sentinel 与其他 Sentinel 保持连接,以便相互检查彼此的可用性并交换消息。然而,您不需要在您运行的每个 Sentinel 实例中配置其他 Sentinel 地址列表,因为 Sentinel 使用 Redis 实例的 Pub/Sub 功能来发现正在监控相同主服务器和副本的其他 Sentinel。
这个功能是通过向名为 sentinel:hello 的频道发送 hello 消息来实现的。
同样,您不需要配置附加到主服务器的副本列表,因为 Sentinel 将自动发现这个列表查询 Redis。
- 每个 Sentinel 每两秒向每个被监控的主服务器和副本的 Pub/Sub 频道 sentinel:hello 发布一条消息,宣布其存在,包括 ip、端口、runid
- 每个 Sentinel 订阅每个主服务器和副本的 Pub/Sub 频道 sentinel:hello,寻找未知的 Sentinel。当检测到新的 Sentinel 时,它们被添加为该主服务器的 Sentinel
- Hello 消息还包括主服务器的完整当前配置。如果接收 Sentinel 的针对给定主服务器的配置比接收到的旧,则它会立即更新到新配置
- 在向主服务器添加新的 Sentinel 之前,Sentinel 总是检查是否已经有具有相同 runid 或相同地址(ip 和端口对)的 Sentinel。在这种情况下,所有匹配的 Sentinel 都会被移除,并添加新的
Sentinel 对故障转移程序之外的实例重新配置
即使没有故障转移正在进行,Sentinel 也会始终尝试在被监控的实例上设置当前配置。具体来说:
- 根据当前配置自称为主服务器的副本,将被配置为副本以与当前主服务器进行复制
- 连接到错误主服务器的副本,将被重新配置以与正确的主服务器进行复制
为了让 Sentinel 重新配置副本,必须观察到错误配置一段时间,该时间段大于用于广播新配置的周期。
这可以防止具有陈旧配置的 Sentinel(例如因为它们刚刚从分区重新加入)在接收更新之前尝试更改副本配置。
还要注意,始终尝试强加当前配置的语义使得故障转移对分区更具有抵抗力:
- 故障转移后的主服务器在恢复可用时被重新配置为副本
- 分区期间分区的副本一旦可达,就会被重新配置
关于这一节需要记住的是:Sentinel 是一个系统,其中每个进程都将始终尝试将最后的逻辑配置强加给一组被监控的实例。
副本选择和优先级
当 Sentinel 实例准备执行故障转移时,由于主服务器处于 ODOWN 状态并且 Sentinel 从已知的大多数 Sentinel 实例那里收到了故障转移的授权,需要选择一个合适的副本。
副本选择过程评估副本的以下信息:
- 与主服务器的断开时间
- 副本优先级
- 处理的复制偏移量
- 运行 ID
发现与主服务器断开时间超过配置的主服务器超时时间(down-after-milliseconds 选项)的十倍,加上从执行故障转移的 Sentinel 视角中主服务器也不可用的时间,超过这个时间的副本被认为不适合故障转移并被跳过。
更严格地说,一个副本的 INFO 输出表明它已经从主服务器断开的时间超过了:
(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
被认为是不可靠的,并完全被忽略。
副本选择仅考虑通过上述测试的副本,并基于上述标准排序,排序顺序如下。
- 副本根据 Redis 实例的 redis.conf 文件中配置的 replica-priority 排序。优先级较低的将被优先选择
- 如果优先级相同,将检查副本处理的复制偏移量,选择从主服务器接收更多数据的副本
- 如果多个副本具有相同的优先级并处理了来自主服务器的相同数据,将进一步检查,选择运行 ID 字典序较小的副本。拥有较低运行 ID 的副本并没有真正的优势,但这有助于使副本选择过程更加确定性,而不是随机选择一个副本
- 在大多数情况下,不需要显式地设置 replica-priority,因此所有实例将使用相同的默认值。如果有特定的故障转移偏好,必须在所有实例上设置 replica-priority,包括主服务器,因为主服务器可能在将来某个时间点变成副本 - 届时它将需要适当的副本优先级设置
Redis 实例可以配置为特殊的副本优先级零,以便永远不会被 Sentinel 选择为新的主服务器。然而,这样配置的副本仍将被 Sentinel 重新配置以复制新的主服务器,唯一的区别是它永远不会成为主服务器。
算法和内部机制
在接下来的部分中,我们将探索 Sentinel 行为的细节。用户并不严格需要了解所有细节,但深入理解 Sentinel 可以帮助更有效地部署和操作 Sentinel。
Quorum
前面的章节显示,每个由 Sentinel 监控的主服务器都与一个配置的Quorum
相关联。它指定了需要同意主服务器不可达或出现错误条件以触发故障转移的 Sentinel 进程数量。
然而,在触发故障转移之后,为了实际执行故障转移,至少要有大多数 Sentinel 授权给 Sentinel 进行故障转移。如果存在少数 Sentinel 的分区,Sentinel 永远不会执行故障转移。
让我们尝试更清晰地说明:
Quorum
:需要检测到错误条件以便将主服务器标记为 ODOWN 的 Sentinel 进程数量- 故障转移由 ODOWN 状态触发
- 一旦触发故障转移,尝试故障转移的 Sentinel 需要向大多数 Sentinel(或如果
Quorum
设置为大于多数的数字,则需要更多)请求授权
这种差异看似微妙,但实际上很简单理解和使用。例如,如果你有 5 个 Sentinel 实例,Quorum
设置为 2,只要 2 个 Sentinel 相信主服务器不可达,就会触发故障转移,然而,两个 Sentinel 中的一个只有在至少得到 3 个 Sentinel 的授权时才能进行故障转移。
如果Quorum
配置为 5,则所有 Sentinel 必须同意主服务器的错误条件,而且需要所有 Sentinel 的授权才能进行故障转移。
这意味着Quorum
可以用两种方式来调整 Sentinel:
- 如果
Quorum
设置为小于我们部署的 Sentinel 多数的值,我们基本上是使 Sentinel 对主服务器故障更敏感,只要即使是少数 Sentinel 也无法与主服务器通信,就触发故障转移 - 如果
Quorum
设置为大于 Sentinel 多数的值,我们使 Sentinel 只有在有非常大数量(大于多数)的连接良好的 Sentinel 同意主服务器已下线时才能进行故障转移
配置epochs
Sentinel 需要从大多数 Sentinel 那里获得授权,才能开始一个故障转移,这是出于几个重要原因:
当一个 Sentinel 获得授权时,它会为其正在故障转移的主服务器获得一个独特的配置epochs号。这是一个数字,将在故障转移完成后用于对新配置进行版本控制。因为大多数 Sentinel 同意给定的版本号分配给特定的 Sentinel,所以其他 Sentinel 将无法使用它。这意味着每次故障转移的每个配置都会有一个独特的版本号。我们将看到为什么这非常重要。
此外,Sentinel 有一个规则:如果一个 Sentinel 为给定主服务器的故障转移投票给了另一个 Sentinel,它将等待一段时间才尝试再次对同一个主服务器进行故障转移。这个延迟是你可以在 sentinel.conf 中配置的 2 倍故障转移超时时间。这意味着 Sentinel 不会同时尝试对同一个主服务器进行故障转移,首先请求授权的将尝试,如果失败,另一个将在一段时间后尝试,依此类推。
Redis Sentinel 保证了活性属性,即如果大多数 Sentinel 能够通信,最终将有一个被授权进行故障转移,如果主服务器宕机的话。
Redis Sentinel 也保证了安全性属性,即每个 Sentinel 将使用不同的配置epochs对同一个主服务器进行故障转移。
配置传播
一旦 Sentinel 成功地对主服务器进行了故障转移,它将开始广播新配置,以便其他 Sentinel 更新它们对给定主服务器的信息。
要使故障转移被视为成功,需要 Sentinel 能够向选定的副本发送 REPLICAOF NO ONE 命令,并且稍后在主服务器的 INFO 输出中观察到切换到主服务器的情况。
此时,即使副本的重新配置还在进行中,故障转移也被视为成功,所有 Sentinel 都需要开始报告新配置。
新配置的传播方式是我们需要每个 Sentinel 故障转移都使用不同版本号(配置epochs)授权的原因。
每个 Sentinel 持续地通过 Redis Pub/Sub 消息,在主服务器和所有副本中广播其对主服务器配置的版本。同时,所有 Sentinel 等待消息,以查看其他 Sentinel 宣传的配置是什么。
配置在 __sentinel__:hello Pub/Sub
频道中广播。
因为每个配置都有不同的版本号,较大的版本号总是胜过较小的版本号。
例如,对于主服务器 mymaster 的配置开始时,所有 Sentinel 都认为主服务器位于 192.168.1.50:6379。这个配置的版本号为 1。一段时间后,一个 Sentinel 被授权进行故障转移,版本号为 2。如果故障转移成功,它将开始广播新配置,假设是 192.168.1.50:9000,版本号为 2。所有其他实例将看到这个配置,并将相应地更新它们的配置,因为新配置具有更大的版本号。
这意味着 Sentinel 保证了第二个活性属性:能够通信的一组 Sentinel 将会收敛到具有更高版本号的相同配置上。
基本上,如果网络被分区,每个分区将收敛到更高的本地配置。在没有分区的特殊情况下,只有一个分区,每个 Sentinel 将就配置达成一致。
一致性与分区
Redis Sentinel 配置最终是一致的,因此每个分区最终都会收敛到可用的更高配置。然而,在实际使用 Sentinel 的系统中,有三种不同的参与者:
- Redis 实例。
- Sentinel 实例。
- 客户端。
为了定义系统的行为,我们必须考虑这三者。
以下是一个简单的网络,其中有 3 个节点,每个节点运行一个 Redis 实例和一个 Sentinel 实例:
+-------------+
| Sentinel 1 |----- 客户端 A
| Redis 1 (M) |
+-------------+
|
|
+-------------+ | +------------+
| Sentinel 2 |-+-- // ----| Sentinel 3 |----- 客户端 B
| Redis 2 (S) | | Redis 3 (M)|
+-------------+ +------------+
在这个系统中,原始状态是 Redis 3 是主节点,而 Redis 1 和 2 是副本。一个分区发生了,隔离了旧的主节点。Sentinel 1 和 2 开始了故障转移,提升 Sentinel 1 成为新的主节点。
Sentinel 属性保证 Sentinel 1 和 2 现在有了主节点的新配置。然而 Sentinel 3 仍然有旧配置,因为它位于不同的分区。
我们知道,当网络分区愈合时,Sentinel 3 将更新其配置,但是在分区期间,如果有客户端与旧主节点一起被分区,会发生什么?
客户端仍然能够向 Redis 3,旧的主节点写入数据。当分区重新连接时,Redis 3 将变成 Redis 1 的副本,分区期间写入的所有数据将丢失。
根据您的配置,您可能希望或不希望这种情况发生:
- 如果你使用 Redis 作为缓存,即使数据会丢失,客户端 B 仍然能够向旧主节点写入数据可能是方便的
- 如果你使用 Redis 作为存储,这不好,你需要配置系统以部分防止这个问题
由于 Redis 是异步复制的,没有办法完全防止这种情况下的数据丢失,但是您可以使用以下 Redis 配置选项限制 Redis 3 和 Redis 1 之间的差异:
min-replicas-to-write 1
min-replicas-max-lag 10
使用上述配置(请参阅 Redis 发行版中自注释的 redis.conf 示例了解更多信息),一个 Redis 实例,在作为主节点时,如果不能至少复制到 1 个副本,将停止接受写入。由于复制是异步的,不能写入实际上意味着副本要么断开连接,要么在指定的 max-lag 秒数以上没有给我们发送异步确认。
使用这种配置,上面例子中的 Redis 3 将在 10 秒后变得不可用。当分区愈合时,Sentinel 3 的配置将收敛到新配置,客户端 B 将能够获取有效配置并继续操作。
总的来说,Redis + Sentinel 整体上是一个最终一致性系统,其合并函数是最后故障转移胜出,旧主节点的数据被丢弃以复制当前主节点的数据,因此总是有一个丢失确认写入的窗口。这是由于 Redis 异步复制和系统的“虚拟”合并函数的丢弃性质。请注意,这不是 Sentinel 本身的限制,如果您使用强一致性复制状态机来协调故障转移,相同的属性仍然适用。避免丢失确认写入只有两种方法:
- 使用同步复制(和适当的共识算法来运行复制的状态机)。
- 使用最终一致性系统,其中同一对象的不同版本可以合并。
Redis 目前无法使用上述任何系统,并且目前不在开发目标之内。然而,有些代理在 Redis 存储之上实现了解决方案“2”,例如 SoundCloud 的 Roshi,或 Netflix 的 Dynomite。
Sentinel 持久状态
Sentinel 状态被持久化在 sentinel 配置文件中。例如,每次收到或创建(领导 Sentinel)主节点的新配置时,配置都会与配置时代一起持久化到磁盘。这意味着停止和重启 Sentinel 进程是安全的。
TILT 模式
Redis Sentinel 高度依赖计算机时间:例如,为了了解一个实例是否可用,它会记住对 PING 命令的最后一次成功回复的时间,并将其与当前时间比较以了解它有多旧。
然而,如果计算机时间以意外的方式变化,或者计算机非常繁忙,或者进程因某种原因被阻塞,Sentinel 可能会以意外的方式开始行为。
TILT 模式是 Sentinel 可以进入的一种特殊“保护”模式,当检测到可能降低系统可靠性的奇怪情况时。Sentinel 定时器中断通常每秒调用 10 次,所以我们期望两次调用定时器中断之间大约会经过 100 毫秒。
Sentinel 所做的是记录上一次调用定时器中断的时间,并与当前调用进行比较:如果时间差异是负数或意外地大(2 秒或更多),则进入 TILT 模式(或如果已经进入,则推迟退出 TILT 模式)。
在 TILT 模式下,Sentinel 将继续监控一切,但是:
- 它停止了所有行动。
- 它开始对 SENTINEL is-master-down-by-addr 请求作出否定回复,因为不再信任检测失败的能力。
- 如果一切看起来正常持续 30 秒,TILT 模式就会退出。
在 Sentinel TILT 模式下,如果我们发送 INFO 命令,我们可能会得到以下响应:
$ redis-cli -p 26379
127.0.0.1:26379> info
(从 Sentinel 服务器跳过的其他信息。)
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_tilt_since_seconds:-1
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=0,sentinels=1
字段 "sentinel_tilt_since_seconds" 表示 Sentinel 已经处于 TILT 模式的秒数。如果它不在 TILT 模式下,该值将是 -1。
请注意,在某些方面,TILT 模式可以使用许多内核提供的单调时钟 API 来替代。然而,目前还不清楚这是否是一个好的解决方案,因为当前系统避免了进程被挂起或长时间不被调度器执行的问题。
关于本手册页中使用的词“slave”的说明:从 Redis 5 开始,如果不是为了向后兼容,Redis 项目不再使用“slave”这个词。不幸的是,在这个命令中,“slave”是协议的一部分,所以我们只有在这个 API 自然过时时进行移除。