用法
valkey-sentinel
/path/to/sentinel.conf
valkey-server
/path/to/sentinel.conf --sentinel
描述
当不使用 Valkey Cluster 时,Valkey Sentinel 为 Valkey 提供高可用性。
Valkey Sentinel 还提供其他辅助任务,如监控、通知,并充当客户端的配置提供者。
以下是 Sentinel 在宏观层面(即总体概览)的全部功能列表:
- 监控。Sentinel 不断检查您的主实例和副本实例是否按预期工作。
- 通知。Sentinel 可以通过 API 通知系统管理员或其他计算机程序,某个受监控的 Valkey 实例出现问题。
- 自动故障转移。如果主实例未按预期工作,Sentinel 可以启动故障转移过程,其中一个副本实例被提升为主实例,其他额外的副本实例被重新配置为使用新的主实例,并且使用 Valkey 服务器的应用程序会收到要连接的新地址通知。
- 配置提供者。Sentinel 作为客户端服务发现的权威来源:客户端连接到 Sentinel 以请求负责给定服务的当前 Valkey 主实例的地址。如果发生故障转移,Sentinel 将报告新地址。
作为分布式系统的 Sentinel
Valkey Sentinel 是一个分布式系统
Sentinel 本身设计为在多个 Sentinel 进程协同工作的配置中运行。多个 Sentinel 进程协同工作的优势如下:
- 当多个 Sentinel 就给定主实例不再可用这一事实达成一致时,会执行故障检测。这降低了误报的可能性。
- 即使并非所有 Sentinel 进程都在工作,Sentinel 也能正常运行,这使得系统能够抵御故障。毕竟,如果故障转移系统本身是单点故障,那就没什么乐趣可言了。
Sentinel、Valkey 实例(主实例和副本实例)以及连接到 Sentinel 和 Valkey 的客户端共同构成一个具有特定属性的更大分布式系统。本文档将逐步介绍概念,从理解 Sentinel 基本属性所需的基本信息,到理解 Sentinel 具体工作原理所需的更复杂(可选)信息。
Sentinel 快速入门
Valkey Sentinel 包含在 Valkey 中。
运行 Sentinel
如果您使用 valkey-sentinel
可执行文件(或者如果您有一个指向 valkey-server
可执行文件的同名符号链接),您可以使用以下命令行运行 Sentinel:
valkey-sentinel /path/to/sentinel.conf
否则,您可以直接使用 valkey-server
可执行文件,以 Sentinel 模式启动它:
valkey-server /path/to/sentinel.conf --sentinel
两种方式效果相同。
然而,运行 Sentinel 时**必须**使用配置文件,因为系统将使用此文件保存当前状态,以便在重启时重新加载。如果未提供配置文件,或者配置文件路径不可写,Sentinel 将拒绝启动。
Sentinel 默认运行在 **TCP 端口 26379** 上监听连接,因此为了使 Sentinel 正常工作,您的服务器的 26379 端口**必须开放**以接收来自其他 Sentinel 实例 IP 地址的连接。否则,Sentinel 之间无法通信,也无法就如何操作达成一致,因此将永远无法执行故障转移。
部署 Sentinel 之前需要了解的基本事项
- 您需要至少三个 Sentinel 实例才能实现稳健部署。
- 这三个 Sentinel 实例应放置在被认为会独立发生故障的计算机或虚拟机中。例如,不同物理服务器或在不同可用区中执行的虚拟机。
- Sentinel + Valkey 分布式系统不保证在故障期间保留已确认的写入,因为 Valkey 使用异步复制。但是,有一些部署 Sentinel 的方法可以将写入丢失的窗口限制在特定时刻,而其他部署方法则安全性较低。
- 您的客户端需要支持 Sentinel。流行的客户端库支持 Sentinel,但并非所有都支持。
- 如果您不时在开发环境(或者更好的是,如果可能的话在生产环境)中测试其是否正常工作,那么任何高可用性(HA)设置都是不安全的。您可能存在配置错误,只有当为时已晚时(例如凌晨 3 点主实例停止工作时)才会显现出来。
- **Sentinel、Docker 或其他形式的网络地址转换(NAT)或端口映射应谨慎混合使用**:Docker 执行端口重映射,这会破坏 Sentinel 自动发现其他 Sentinel 进程以及主实例的副本列表的功能。有关更多信息,请查阅本文档后面关于《Sentinel 与 Docker》的部分。
配置 Sentinel
Valkey 源代码分发包中包含一个名为 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 时,配置也会被重写。
上述示例配置基本监控了两组 Valkey 实例,每组都由一个主实例和不确定数量的副本实例组成。其中一组实例名为 mymaster
,另一组名为 resque
。
sentinel monitor
语句的参数含义如下:
sentinel monitor <primary-name> <ip> <port> <quorum>
为了清楚起见,我们逐行检查配置选项的含义:
第一行用于告诉 Valkey 监控一个名为 *mymaster* 的主实例,该主实例的地址是 127.0.0.1,端口是 6379,法定人数为 2。除了**法定人数**参数外,一切都非常明显。
- **法定人数**是指需要就主实例不可达这一事实达成一致的 Sentinel 数量,以便真正将主实例标记为故障,并在可能的情况下最终启动故障转移程序。
- 然而,**法定人数仅用于检测故障**。为了实际执行故障转移,其中一个 Sentinel 需要被选举为故障转移的领导者并获得授权进行。这只有在**多数 Sentinel 进程**投票同意的情况下才会发生。
例如,如果您有 5 个 Sentinel 进程,并且给定主实例的法定人数设置为 2,那么会发生以下情况:
- 如果两个 Sentinel 同时就主实例不可达达成一致,其中一个将尝试启动故障转移。
- 如果总共有至少三个 Sentinel 可达,则故障转移将被授权并实际启动。
实际意义上,这意味着在故障期间,**如果大多数 Sentinel 进程无法通信,Sentinel 绝不会启动故障转移**(即在少数派分区中不会发生故障转移)。
其他 Sentinel 选项
其他选项几乎总是以下形式:
sentinel <option_name> <primary_name> <option_value>
用于以下目的:
down-after-milliseconds
是 Sentinel 开始认为实例下线之前,该实例不应可达(不回复我们的 PING 请求或回复错误)的毫秒数。parallel-syncs
设置了故障转移后可以同时重新配置以使用新主实例的副本数量。数字越小,故障转移过程完成所需的时间就越长;但是,如果副本配置为提供旧数据,您可能不希望所有副本同时与主实例重新同步。虽然复制过程对副本来说大部分是非阻塞的,但有一个时刻它会停止从主实例加载批量数据。您可能希望通过将此选项设置为 1 来确保一次只有一个副本不可达。
其他选项将在本文档的其余部分进行描述,并在 Valkey 分发包中附带的示例 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" |
+--------------------+
我们在方框内写明它们正在运行的内容。
+--------------------+
| Valkey primary M1 |
| Valkey Sentinel S1 |
+--------------------+
不同的方框通过线条连接,表示它们能够通信。
+-------------+ +-------------+
| 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 |
+----+ +----+
Configuration: quorum = 1
- 在此设置中,如果主实例 M1 失败,R1 将被提升,因为两个 Sentinel 可以就故障达成一致(显然法定人数设置为 1),并且也可以授权故障转移,因为多数派是两个。因此,表面上看它似乎可以工作,但请查看以下几点,了解为什么此设置是损坏的。
- 如果运行 M1 的盒子停止工作,S1 也会停止工作。在另一个盒子 S2 中运行的 Sentinel 将无法授权故障转移,因此系统将变得不可用。
请注意,需要多数派才能对不同的故障转移进行排序,并随后将最新配置传播到所有 Sentinel。另请注意,在上述设置的单侧进行故障转移而没有任何协议是非常危险的:
+----+ +------+
| M1 |----//-----| [M1] |
| S1 | | S2 |
+----+ +------+
在上述配置中,我们以完全对称的方式创建了两个主实例(假设 S2 可以在未经授权的情况下进行故障转移)。客户端可能无限期地向两侧写入数据,并且无法在分区恢复时判断哪种配置是正确的,以防止出现**永久性脑裂**。
因此,请始终**在三个不同的盒子中至少部署三个 Sentinel**。
示例 2:三个盒子的基本设置
这是一个非常简单的设置,其优点是易于调整以提高安全性。它基于三个盒子,每个盒子都运行一个 Valkey 进程和一个 Sentinel 进程。
+----+
| M1 |
| S1 |
+----+
|
+----+ | +----+
| R2 |----+----| R3 |
| S2 | | S3 |
+----+ +----+
Configuration: quorum = 2
如果主实例 M1 失败,S2 和 S3 将就故障达成一致,并能够授权故障转移,使客户端能够继续工作。
在每个 Sentinel 设置中,由于 Valkey 使用异步复制,总会有丢失某些写入的风险,因为某个已确认的写入可能无法到达被提升为主实例的副本。然而在上述设置中,由于客户端与旧主实例被分区隔离,风险更高,如下图所示:
+----+
| M1 |
| S1 | <- C1 (writes will be lost)
+----+
|
/
/
+------+ | +----+
| [M2] |----+----| R3 |
| S2 | | S3 |
+------+ +----+
在这种情况下,网络分区隔离了旧主实例 M1,因此副本 R2 被提升为主实例。然而,与旧主实例位于同一分区中的客户端(如 C1)可能会继续向旧主实例写入数据。这些数据将永久丢失,因为当分区恢复时,该主实例将被重新配置为新主实例的副本,并丢弃其数据集。
可以使用以下 Valkey 复制功能来缓解此问题:如果主实例检测到它不再能够将其写入传输到指定数量的副本,则停止接受写入。
min-replicas-to-write 1
min-replicas-max-lag 10
通过上述配置(有关更多信息,请参阅 Valkey 分发包中带自注释的 valkey.conf
示例),Valkey 实例在充当主实例时,如果无法向至少 1 个副本写入数据,将停止接受写入。由于复制是异步的,**无法写入**实际上意味着副本已断开连接,或者在超过指定 max-lag
秒数后未向我们发送异步确认。
使用此配置,上述示例中的旧 Valkey 主实例 M1 将在 10 秒后变得不可用。当分区恢复时,Sentinel 配置将收敛到新配置,客户端 C1 将能够获取有效配置并继续使用新的主实例。
然而,天下没有免费的午餐。通过这种改进,如果两个副本都下线,主实例将停止接受写入。这是一个权衡。
示例 3:客户端盒子中的 Sentinel
有时我们只有两个 Valkey 盒子可用,一个用于主实例,一个用于副本实例。在这种情况下,示例 2 中的配置不可行,因此我们可以采用以下方法,将 Sentinel 放置在客户端所在的位置:
+----+ +----+
| M1 |----+----| R1 |
| | | | |
+----+ | +----+
|
+------------+------------+
| | |
| | |
+----+ +----+ +----+
| C1 | | C2 | | C3 |
| S1 | | S2 | | S3 |
+----+ +----+ +----+
Configuration: quorum = 2
在此设置中,Sentinel 的视角与客户端相同:如果主实例可被大多数客户端访问,则它就是正常的。此处的 C1、C2、C3 是通用客户端,C1 并不意味着标识连接到 Valkey 的单个客户端。它更可能是一个应用程序服务器、一个 Rails 应用或类似的东西。
如果运行 M1 和 S1 的盒子发生故障,故障转移将顺利进行,但很容易看出,不同的网络分区将导致不同的行为。例如,如果客户端和 Valkey 服务器之间的网络断开,Sentinel 将无法设置,因为 Valkey 主实例和副本实例都将不可用。
请注意,如果 C3 与 M1 分区(在上述网络中很难发生,但在不同布局或软件层故障时更有可能发生),我们将遇到与示例 2 中类似的问题,不同之处在于此处我们无法打破对称性,因为只有一个副本实例和一个主实例,因此主实例在与其副本实例断开连接时不能停止接受查询,否则在副本实例故障期间主实例将永远不可用。
因此,这是一个有效的设置,但示例 2 中的设置具有优势,例如 Valkey 的高可用性系统与 Valkey 本身在相同的盒子中运行,这可能更易于管理,并且能够限制少数派分区中的主实例接收写入的时间。
示例 4:少于三个客户端的 Sentinel 客户端侧
如果客户端侧(例如三个 Web 服务器)的盒子少于三个,则无法使用示例 3 中描述的设置。在这种情况下,我们需要采用以下混合设置:
+----+ +----+
| M1 |----+----| R1 |
| S1 | | | S2 |
+----+ | +----+
|
+------+-----+
| |
| |
+----+ +----+
| C1 | | C2 |
| S3 | | S4 |
+----+ +----+
Configuration: quorum = 3
这与示例 3 中的设置类似,但此处我们在可用的四个盒子中运行四个 Sentinel。如果主实例 M1 变为不可用,其他三个 Sentinel 将执行故障转移。
理论上,通过移除运行 C2 和 S4 的盒子并将法定人数设置为 2,此设置也能工作。然而,我们不太可能在 Valkey 侧实现高可用性而不期望在应用程序层也具有高可用性。
Sentinel、Docker、NAT 和可能的问题
Docker 使用一种称为端口映射的技术:在 Docker 容器内运行的程序可能会以与程序认为正在使用的端口不同的端口暴露。这对于在同一服务器上同时运行多个使用相同端口的容器很有用。
Docker 并非唯一发生这种情况的软件系统,还有其他网络地址转换(NAT)设置,其中端口可能会被重新映射,有时不仅是端口,IP 地址也会被重新映射。
重新映射端口和地址会通过两种方式给 Sentinel 带来问题:
- Sentinel 对其他 Sentinel 的自动发现不再有效,因为它基于**`hello` 消息**,每个 Sentinel 在其中宣布它们监听连接的端口和 IP 地址。然而,Sentinel 无法理解某个地址或端口是否被重新映射,因此它宣布的信息对于其他 Sentinel 连接来说是不正确的。
- 副本实例在 Valkey 主实例的
INFO
输出中以类似方式列出:地址由主实例检查 TCP 连接的远程对等体来检测,而端口由副本实例在握手期间自行公布,然而由于与第 1 点相同的原因,端口可能不正确。
由于 Sentinel 使用主实例的 INFO
输出信息自动检测副本实例,检测到的副本实例将无法访问,并且 Sentinel 将永远无法对主实例进行故障转移,因为从系统角度来看没有可用的良好副本实例,因此目前无法使用 Sentinel 监控通过 Docker 部署的一组主实例和副本实例,**除非您指示 Docker 进行 1:1 的端口映射**。
对于第一个问题,如果您想使用 Docker 转发端口(或任何其他端口被重新映射的 NAT 设置)运行一组 Sentinel 实例,您可以使用以下两个 Sentinel 配置指令来强制 Sentinel 宣布特定的 IP 和端口集:
sentinel announce-ip <ip>
sentinel announce-port <port>
请注意,Docker 能够以**主机网络模式**运行(有关更多信息,请查看 --net=host
选项)。在这种设置中,端口不会被重新映射,因此不应产生问题。
IP 地址和 DNS 名称
旧版本的 Sentinel 不支持主机名,并且需要在所有地方指定 IP 地址。从 6.2 版开始,Sentinel **可选**支持主机名。
此功能默认禁用。如果您要启用 DNS/主机名支持,请注意:
- 您的 Valkey 和 Sentinel 节点上的名称解析配置必须可靠,并且能够快速解析地址。地址解析中的意外延迟可能会对 Sentinel 产生负面影响。
- 您应该在所有地方都使用主机名,并避免混合使用主机名和 IP 地址。为此,请分别对所有 Valkey 和 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》、配置和语义的所有详细信息。然而,对于希望尽快使用系统的人来说,本节是一个教程,展示了如何配置 3 个 Sentinel 实例并与之交互。
这里我们假设实例在端口 5000、5001、5002 上执行。我们还假设您有一个正在运行的 Valkey 主实例在端口 6379 上,并有一个副本实例在端口 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 可以同时监控不同的主实例和副本实例集。 - 法定人数设置为 2(
sentinel monitor
配置指令的最后一个参数)。 down-after-milliseconds
的值是 5000 毫秒,即 5 秒,因此只要在此时间内我们没有收到任何 ping 回复,主实例就会被检测为故障。
一旦您启动三个 Sentinel,您会看到它们记录的一些消息,例如:
+monitor master mymaster 127.0.0.1 6379 quorum 2
这是一个 Sentinel 事件,如果您订阅了《发布/订阅消息》部分中指定的事件名称,您可以通过发布/订阅接收此类事件。
Sentinel 在故障检测和故障转移期间生成并记录不同的事件。
询问 Sentinel 主实例的状态
使用 Sentinel 开始最明显的事情是检查其监控的主实例是否运行良好:
$ valkey-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 已经为此主实例检测到另外两个 Sentinel。如果您检查日志,您将看到生成的+sentinel
事件。flags
只是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 部署已准备好进行测试。我们可以直接杀死我们的主实例,检查配置是否更改。为此,我们可以这样做:
valkey-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 是正常的 Valkey 端口)。Sentinel 使用 Valkey 协议接受命令,因此您可以使用 valkey-cli
或任何其他未修改的 Valkey 客户端与 Sentinel 通信。
可以直接查询 Sentinel,以了解从其角度来看受监控的 Valkey 实例的状态,查看它知道的其他 Sentinel 等等。或者,使用发布/订阅(Pub/Sub),可以从 Sentinel 接收**推送式**通知,每当发生故障转移、实例进入错误状态等事件时都会收到通知。
Sentinel 命令
SENTINEL
命令是 Sentinel 的主要 API。以下是其子命令列表(适用时会注明最低版本):
- SENTINEL CONFIG GET
<name>
(>= 6.2
) 获取全局 Sentinel 配置参数的当前值。指定的名称可以是通配符,类似于 Valkey 的CONFIG GET
命令。 - SENTINEL CONFIG SET
<name>
<value>
(>= 6.2
) 设置全局 Sentinel 配置参数的值。 - SENTINEL CKQUORUM
<primary name>
检查当前 Sentinel 配置是否能够达到故障转移主实例所需的法定人数,以及授权故障转移所需的多数派。此命令应用于监控系统以检查 Sentinel 部署是否正常。 - SENTINEL FLUSHCONFIG 强制 Sentinel 将其配置(包括当前 Sentinel 状态)重写到磁盘。通常,Sentinel 每次状态发生变化时(在重新启动后持久保存的状态子集的上下文中)都会重写配置。然而,有时由于操作错误、磁盘故障、软件包升级脚本或配置管理器,配置文件可能会丢失。在这种情况下,强制 Sentinel 重写配置文件的方法会很方便。即使先前的配置文件完全丢失,此命令也能工作。
- SENTINEL FAILOVER
<primary name>
强制执行故障转移,就像主实例不可达一样,且不向其他 Sentinel 请求同意(但是将发布新版本的配置,以便其他 Sentinel 更新其配置)。 - SENTINEL GET-MASTER-ADDR-BY-NAME
<primary name>
返回具有该名称的主实例的 IP 和端口号。如果此主实例正在进行故障转移或已成功完成,则返回被提升的副本实例的地址和端口。 - SENTINEL INFO-CACHE 返回主实例和副本实例缓存的
INFO
输出。 - SENTINEL IS-MASTER-DOWN-BY-ADDR
- SENTINEL MASTER
<primary name>
显示指定主实例的状态和信息。 - SENTINEL MASTERS 显示受监控的主实例列表及其状态。
- SENTINEL MONITOR 启动 Sentinel 的监控。有关更多信息,请参阅《在运行时重新配置 Sentinel》部分。
- SENTINEL MYID (
>= 6.2
) 返回 Sentinel 实例的 ID。 - SENTINEL PENDING-SCRIPTS 此命令返回有关挂起脚本的信息。
- SENTINEL REMOVE 停止 Sentinel 的监控。有关更多信息,请参阅《在运行时重新配置 Sentinel》部分。
- SENTINEL REPLICAS
<primary name>
显示此主实例的副本实例列表及其状态。 - SENTINEL SENTINELS
<primary name>
显示此主实例的 Sentinel 实例列表及其状态。 - SENTINEL SET 设置 Sentinel 的监控配置。有关更多信息,请参阅《在运行时重新配置 Sentinel》部分。
- SENTINEL SIMULATE-FAILURE (crash-after-election|crash-after-promotion|help) 此命令模拟不同的 Sentinel 崩溃场景。
- SENTINEL RESET
<pattern>
此命令将重置所有匹配名称的主实例。pattern 参数是 glob 风格的模式。重置过程会清除主实例中的任何先前状态(包括正在进行的故障转移),并移除所有已发现并与主实例关联的副本实例和 Sentinel。
为了连接管理和管理目的,Sentinel 支持 Valkey 命令的以下子集:
- ACL (
>= 6.2
) 此命令管理 Sentinel 访问控制列表。有关更多信息,请参阅ACL 文档页面和《Sentinel 访问控制列表认证》。 - AUTH 验证客户端连接。有关更多信息,请参阅
AUTH
命令和《使用认证配置 Sentinel 实例》部分。 - CLIENT 此命令管理客户端连接。有关更多信息,请参阅其子命令页面。
- COMMAND (
>= 6.2
) 此命令返回有关命令的信息。有关更多信息,请参阅COMMAND
命令及其各种子命令。 - HELLO (
>= 6.0
) 切换连接协议。有关更多信息,请参阅HELLO
命令。 - INFO 返回有关 Sentinel 服务器的信息和统计数据。有关更多信息,请参阅
INFO
命令。 - PING 此命令仅返回 PONG。
- ROLE 此命令返回字符串“sentinel”和受监控的主实例列表。有关更多信息,请参阅
ROLE
命令。 - SHUTDOWN 关闭 Sentinel 实例。
最后,Sentinel 还支持 SUBSCRIBE
、UNSUBSCRIBE
、PSUBSCRIBE
和 PUNSUBSCRIBE
命令。有关更多详细信息,请参阅《发布/订阅消息》部分。
在运行时重新配置 Sentinel
Sentinel 提供了一个 API,用于添加、移除或更改给定主实例的配置。请注意,如果您有多个 Sentinel,您应该将更改应用到所有实例,以使 Valkey 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 命令与 Valkey 的CONFIG SET
命令非常相似,用于更改特定主实例的配置参数。可以指定多个选项/值对(或不指定)。所有可以通过sentinel.conf
配置的配置参数也可以使用 SET 命令进行配置。
以下是 SENTINEL SET
命令的示例,用于修改名为 objects-cache
的主实例的 down-after-milliseconds
配置:
SENTINEL SET objects-cache-master down-after-milliseconds 1000
如前所述,SENTINEL SET
可以用于设置启动配置文件中所有可设置的配置参数。此外,无需通过 SENTINEL REMOVE
后跟 SENTINEL MONITOR
来移除并重新添加主实例,只需使用以下命令即可更改主实例的法定人数配置:
SENTINEL SET objects-cache-master quorum 5
请注意,没有等效的 GET 命令,因为 SENTINEL MASTER
以易于解析的格式(作为字段/值对数组)提供所有配置参数。
Sentinel 还允许获取和设置全局配置参数,这些参数在此之前仅在配置文件中支持。
- SENTINEL CONFIG GET
<name>
获取全局 Sentinel 配置参数的当前值。指定的名称可以是通配符,类似于 Valkey 的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 <primary name>
来检查所有 Sentinel 是否就监控主实例的 Sentinel 总数达成一致。
移除 Sentinel 稍微复杂一些:**Sentinel 永远不会忘记已发现的 Sentinel**,即使它们长时间无法访问,因为我们不希望动态改变授权故障转移和创建新配置编号所需的多数派。因此,在没有网络分区的情况下,应执行以下步骤来移除 Sentinel:
- 停止您要移除的 Sentinel 进程。
- 向所有其他 Sentinel 实例发送
SENTINEL RESET *
命令(如果只想重置单个主实例,可以用确切的主实例名称替换*
)。一个接一个地发送,每个实例之间至少等待 30 秒。 - 通过检查每个 Sentinel 的
SENTINEL MASTER <primary name>
命令输出,检查所有 Sentinel 是否就当前活动的 Sentinel 数量达成一致。
移除旧主实例或不可达的副本实例
Sentinel 永远不会忘记给定主实例的副本实例,即使它们长时间无法访问。这很有用,因为 Sentinel 应该能够在网络分区或故障事件后正确地重新配置返回的副本实例。
此外,在故障转移之后,发生故障转移的主实例实际上会作为新主实例的副本实例添加,这样一旦它再次可用,就会被重新配置为与新主实例复制。
然而,有时您希望将副本实例(可能是旧主实例)从 Sentinel 监控的副本实例列表中永久移除。
为此,您需要向所有 Sentinel 发送 SENTINEL RESET <primary name>
命令:它们将在接下来的 10 秒内刷新副本实例列表,仅添加当前主实例 INFO
输出中列出的正确复制的副本实例。
发布/订阅消息
客户端可以使用 Sentinel 作为 Valkey 兼容的发布/订阅服务器(但您不能使用 PUBLISH
),以便 SUBSCRIBE
或 PSUBSCRIBE
到通道并接收特定事件的通知。
通道名称与事件名称相同。例如,名为 +sdown
的通道将接收所有与实例进入 SDOWN
(SDOWN 意味着从您查询的 Sentinel 的角度来看,实例不再可达)状态相关的通知。
要获取所有消息,只需使用 PSUBSCRIBE *
订阅即可。
以下是您可以使用此 API 接收的通道和消息格式列表。第一个词是通道/事件名称,其余部分是数据格式。
注意:指定**实例详情**的地方表示提供了以下参数来标识目标实例:
<instance-type> <name> <ip> <port> @ <primary-name> <primary-ip> <primary-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
<primary name> <oldip> <oldport> <newip> <newport>
-- 配置更改后,主实例的新 IP 和地址是指定的地址。这是**大多数外部用户感兴趣的消息**。 - +tilt -- 进入倾斜模式。
- -tilt -- 退出倾斜模式。
处理 -BUSY 状态
当 Lua 脚本运行时间超过配置的 Lua 脚本时间限制时,Valkey 实例会返回 -BUSY 错误。当发生这种情况时,在触发故障转移之前,Valkey Sentinel 将尝试发送 SCRIPT KILL
命令,该命令仅在脚本为只读时才能成功。
如果此尝试后实例仍处于错误状态,它最终将被故障转移。
副本实例优先级
Valkey 实例有一个名为 replica-priority
的配置参数。此信息由 Valkey 副本实例在其 INFO
输出中暴露,Sentinel 使用它来选择可用于对主实例进行故障转移的副本实例:
- 如果副本优先级设置为 0,则该副本永远不会被提升为主实例。
- Sentinel 优先选择**较低**优先级的副本实例。
例如,如果当前主实例的同一数据中心中有一个副本 S1,而在另一个数据中心中有一个副本 S2,可以将 S1 的优先级设置为 10,S2 的优先级设置为 100,这样如果主实例发生故障且 S1 和 S2 都可用,S1 将被优先选择。
有关选择副本实例方式的更多信息,请查看本文档的《副本实例选择和优先级》部分。
Sentinel 和 Valkey 认证
当主实例配置为要求客户端进行认证时,作为安全措施,副本实例也需要知道凭据,以便与主实例进行认证并创建用于异步复制协议的主-副本连接。
Valkey 访问控制列表认证
用户认证和权限由访问控制列表 (ACL) 管理。
为了让 Sentinel 连接到配置了 ACL 的 Valkey 服务器实例,Sentinel 配置必须包含以下指令:
sentinel auth-user <primary-name> <username>
sentinel auth-pass <primary-name> <password>
其中 <username>
和 <password>
是访问该组实例的用户名和密码。这些凭据应在所有该组的 Valkey 实例上配置,并具有最小的控制权限。例如:
127.0.0.1:6379> ACL SETUSER sentinel-user ON >somepassword allchannels +multi +slaveaof +ping +exec +subscribe +config|rewrite +role +publish +info +client|setname +client|kill +script|kill
Valkey 仅密码认证
在引入 ACL 之前,可以使用以下配置指令实现认证:
- 主实例中的
requirepass
,用于设置认证密码,并确保实例不会处理未经认证客户端的请求。 - 副本实例中的
masterauth
,以便副本实例与主实例进行认证,从而正确地从主实例复制数据。
当使用 Sentinel 时,不存在单个主实例,因为在故障转移后,副本实例可能会扮演主实例的角色,旧的主实例可以被重新配置为充当副本实例,因此您需要做的是在所有实例(包括主实例和副本实例)中设置上述指令。
这通常也是一个合理的设置,因为您不希望只在主实例中保护数据,而让相同的数据在副本实例中可访问。
然而,在极少数情况下,如果您需要一个无需认证即可访问的副本实例,您仍然可以通过设置**副本优先级为零**来实现,以防止此副本实例被提升为主实例,并且仅在此副本实例中配置 masterauth
指令,而不使用 requirepass
指令,这样未认证的客户端将能够读取数据。
为了让 Sentinel 连接到配置了 requirepass
的 Valkey 服务器实例,Sentinel 配置必须包含 sentinel auth-pass
指令,格式如下:
sentinel auth-pass <primary-name> <password>
使用认证配置 Sentinel 实例
Sentinel 实例本身可以通过要求客户端通过 AUTH
命令进行认证来保护。从 Redis OSS 6.2 开始,提供了访问控制列表 (ACL),而旧版本支持仅密码认证。
请注意,Sentinel 的认证配置应**应用于您部署中的每个实例**,并且**所有实例应使用相同的配置**。此外,ACL 和仅密码认证不应同时使用。
Sentinel 访问控制列表认证
使用 ACL 保护 Sentinel 实例的第一步是防止任何未经授权的访问。为此,您需要禁用默认的超级用户(或至少为其设置一个强密码),并创建一个新用户并允许其访问发布/订阅通道:
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 客户端的文档。
Sentinel 仅密码认证
要将 Sentinel 与仅密码认证一起使用,请在**所有** Sentinel 实例中添加 requirepass
配置指令,如下所示:
requirepass "your_password_here"
以这种方式配置后,Sentinel 将做两件事:
- 客户端向 Sentinel 发送命令时将需要密码。这很明显,因为这种配置指令在 Valkey 中通常就是这样工作的。
- 此外,配置为访问本地 Sentinel 的相同密码将由该 Sentinel 实例用于向其连接的所有其他 Sentinel 实例进行认证。
这意味着**您必须在所有 Sentinel 实例中配置相同的 requirepass
密码**。这样,每个 Sentinel 都可以与其他 Sentinel 通信,而无需为每个 Sentinel 配置访问所有其他 Sentinel 的密码,这非常不切实际。
在使用此配置之前,请确保您的客户端库可以向 Sentinel 实例发送 AUTH
命令。
Sentinel 客户端实现
Sentinel 需要显式的客户端支持,除非系统配置为执行脚本以实现所有请求到新主实例的透明重定向(虚拟 IP 或其他类似系统)。客户端库实现的主题在《Sentinel 客户端指南》文档中有所介绍。
更高级的概念
在以下章节中,我们将探讨 Sentinel 行为的更多细节。用户并非严格需要了解所有细节,但深入理解 Sentinel 可能有助于更有效地部署和操作 Sentinel。
主观下线(SDOWN)和客观下线(ODOWN)故障状态
Valkey 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 认为 Valkey 实例不可用。要触发故障转移,必须达到 ODOWN 状态。
从 SDOWN 切换到 ODOWN 不需要强共识算法,而只是一种流言(gossip)形式:如果给定 Sentinel 收到足够多的 Sentinel **在给定时间范围内**报告主实例不工作,则 SDOWN 会升级为 ODOWN。如果此确认后来缺失,则该标志将被清除。
为了真正启动故障转移,需要更严格的授权,该授权使用实际的多数派,但没有达到 ODOWN 状态就无法触发故障转移。
ODOWN 状态**仅适用于主实例**。对于其他类型的实例,Sentinel 不需要采取行动,因此副本实例和其他 Sentinel 永远不会达到 ODOWN 状态,而只会有 SDOWN 状态。
然而,SDOWN 也有语义含义。例如,处于 SDOWN 状态的副本实例不会被执行故障转移的 Sentinel 选中进行提升。
Sentinel 和副本实例自动发现
Sentinel 之间保持连接,以便相互检查可用性并交换消息。但是,您无需在每个运行的 Sentinel 实例中配置其他 Sentinel 地址列表,因为 Sentinel 使用 Valkey 实例的发布/订阅功能来发现监控相同主实例和副本实例的其他 Sentinel。
此功能通过向名为 __sentinel__:hello
的通道发送**`hello` 消息**来实现。
同样,您无需配置附加到主实例的副本实例列表,因为 Sentinel 会通过查询 Valkey 自动发现此列表。
- 每个 Sentinel 每两秒向每个受监控的主实例和副本实例的发布/订阅通道
__sentinel__:hello
发布一条消息,宣布其 IP、端口、运行 ID。 - 每个 Sentinel 都订阅了每个主实例和副本实例的发布/订阅通道
__sentinel__:hello
,寻找未知的 Sentinel。当检测到新的 Sentinel 时,它们将作为此主实例的 Sentinel 添加。 - `hello` 消息还包含主实例的完整当前配置。如果接收 Sentinel 的给定主实例配置比收到的旧,它会立即更新到新配置。
- 在将新 Sentinel 添加到主实例之前,Sentinel 总是检查是否已存在具有相同运行 ID 或相同地址(IP 和端口对)的 Sentinel。在这种情况下,所有匹配的 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
被认为不可靠并完全被忽略。
副本实例选择只考虑通过上述测试的副本实例,并根据以下标准进行排序:
- 副本实例按 Valkey 实例的
valkey.conf
文件中配置的replica-priority
排序。优先级较低的将被优先选择。 - 如果优先级相同,则检查副本实例已处理的复制偏移量,并选择从主实例接收到更多数据的副本实例。
- 如果多个副本实例具有相同的优先级并从主实例处理了相同的数据,则会执行进一步检查,选择运行 ID 按字典顺序较小的副本实例。运行 ID 较低对副本实例没有实际优势,但有助于使副本实例选择过程更具确定性,而不是诉诸于选择随机副本实例。
在大多数情况下,无需显式设置 replica-priority
,因此所有实例都将使用相同的默认值。如果存在特定的故障转移偏好,则必须在所有实例(包括主实例)上设置 replica-priority
,因为主实例将来可能成为副本实例——届时它将需要正确的 replica-priority
设置。
Valkey 实例可以配置特殊的 replica-priority
值为零,以便 Sentinel **永远不选择**它作为新的主实例。但是,以这种方式配置的副本实例仍将在故障转移后被 Sentinel 重新配置为与新主实例复制,唯一的区别是它永远不会成为主实例本身。
算法和内部机制
在以下章节中,我们将深入探讨 Sentinel 行为的细节。用户并非严格需要了解所有细节,但深入理解 Sentinel 可能有助于更有效地部署和操作 Sentinel。
法定人数
前几节展示了每个 Sentinel 监控的主实例都与一个配置的**法定人数**相关联。它指定了需要就主实例的不可达或错误状态达成一致的 Sentinel 进程数量,才能触发故障转移。
然而,在故障转移被触发后,为了实际执行故障转移,**至少需要多数 Sentinel 授权该 Sentinel 进行故障转移**。Sentinel 绝不会在存在少数派 Sentinel 的分区中执行故障转移。
让我们试着把事情弄得更清楚一些:
- 法定人数:需要检测到错误状态才能将主实例标记为 **ODOWN** 的 Sentinel 进程数量。
- 故障转移由 **ODOWN** 状态触发。
- 一旦故障转移被触发,尝试执行故障转移的 Sentinel 需要向大多数 Sentinel 请求授权(如果法定人数设置为大于多数派的数字,则需要更多授权)。
这种差异可能看起来很微妙,但实际上非常简单易懂和使用。例如,如果您有 5 个 Sentinel 实例,并且法定人数设置为 2,则只要有 2 个 Sentinel 认为主实例不可达,就会触发故障转移;然而,这两个 Sentinel 中的一个只有在获得至少 3 个 Sentinel 的授权后才能执行故障转移。
如果法定人数设置为 5,则所有 Sentinel 都必须就主实例的错误状态达成一致,并且需要所有 Sentinel 的授权才能进行故障转移。
这意味着法定人数可以通过两种方式来调整 Sentinel:
- 如果法定人数设置为小于我们部署的 Sentinel 多数派的值,我们基本上是让 Sentinel 对主实例故障更敏感,只要少数派 Sentinel 无法与主实例通信,就会触发故障转移。
- 如果法定人数设置为大于 Sentinel 多数派的值,我们则使得 Sentinel 只有在有大量(大于多数派)连接良好的 Sentinel 同意主实例下线时才能进行故障转移。
配置纪元
Sentinel 需要获得多数派的授权才能启动故障转移,原因有以下几点:
当 Sentinel 被授权时,它将获得一个唯一的**配置纪元**,用于正在进行故障转移的主实例。这是一个将用于对故障转移完成后新配置进行版本控制的数字。由于多数派同意将给定版本分配给给定 Sentinel,因此其他 Sentinel 将无法使用它。这意味着每个故障转移的每个配置都将使用一个唯一的版本进行版本控制。我们将看到这为何如此重要。
此外,Sentinel 有一个规则:如果一个 Sentinel 投票给另一个 Sentinel 以便对给定主实例进行故障转移,它将等待一段时间才能再次尝试对同一主实例进行故障转移。此延迟可在 sentinel.conf
中配置为 2 * failover-timeout
。这意味着 Sentinel 不会同时尝试对同一主实例进行故障转移,第一个请求授权的将尝试,如果失败,另一个将在一段时间后尝试,依此类推。
Valkey Sentinel 保证了**活性**属性,即如果大多数 Sentinel 能够通信,那么如果主实例下线,最终会有一个 Sentinel 被授权进行故障转移。
Valkey Sentinel 还保证了**安全性**属性,即每个 Sentinel 将使用不同的**配置纪元**对同一主实例进行故障转移。
配置传播
一旦 Sentinel 成功对主实例进行故障转移,它将开始广播新配置,以便其他 Sentinel 更新其关于给定主实例的信息。
要使故障转移被认为是成功的,它要求 Sentinel 能够向选定的副本实例发送 REPLICAOF NO ONE
命令,并且随后在主实例的 INFO
输出中观察到切换为主实例。
此时,即使副本实例的重新配置正在进行中,故障转移也被认为是成功的,并且所有 Sentinel 都需要开始报告新配置。
新配置传播的方式是我们为什么需要每个 Sentinel 故障转移都以不同的版本号(配置纪元)获得授权的原因。
每个 Sentinel 都通过 Valkey 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 都将就配置达成一致。
分区下的一致性
Valkey Sentinel 配置最终一致,因此每个分区都将收敛到可用的更高配置。然而,在实际使用 Sentinel 的系统中,有三种不同的参与者:
- Valkey 实例。
- Sentinel 实例。
- 客户端。
为了定义系统的行为,我们必须考虑这三者。
下面是一个简单的网络,其中有 3 个节点,每个节点都运行着一个 Valkey 实例和一个 Sentinel 实例:
+--------------+
| Sentinel 1 |----- Client A
| Valkey 1 (M) |
+--------------+
|
|
+--------------+ | +-------------+
| Sentinel 2 |----+-- // ----| Sentinel 3 |----- Client B
| Valkey 2 (S) | | Valkey 3 (M)|
+--------------+ +-------------+
在这个系统中,最初的状态是 Valkey 3 是主服务器,而 Valkey 1 和 2 是副本。发生了分区,隔离了旧的主服务器。Sentinel 1 和 2 开始了故障转移,将 Sentinel 1 提升为新的主服务器。
Sentinel 的属性保证 Sentinel 1 和 2 现在拥有主服务器的新配置。然而,Sentinel 3 仍然使用旧配置,因为它位于不同的分区中。
我们知道当网络分区恢复时,Sentinel 3 的配置将会更新,然而,如果在分区期间有客户端与旧主服务器一同被分区,会发生什么?
客户端仍然能够写入 Valkey 3,即旧的主服务器。当分区重新连接时,Valkey 3 将变成 Valkey 1 的副本,并且在分区期间写入的所有数据都将丢失。
根据您的配置,您可能希望或不希望发生这种情况。
- 如果您将 Valkey 用作缓存,那么客户端 B 仍然能够写入旧主服务器可能很方便,即使其数据会丢失。
- 如果您将 Valkey 用作存储,这不是一个好现象,您需要配置系统以部分预防此问题。
由于 Valkey 是异步复制的,在这种情况下无法完全防止数据丢失,但是您可以使用以下 Valkey 配置选项来限制 Valkey 3 和 Valkey 1 之间的差异:
min-replicas-to-write 1
min-replicas-max-lag 10
通过上述配置(有关更多信息,请参阅 Valkey 分发包中带自注释的 valkey.conf
示例),Valkey 实例在充当主实例时,如果无法向至少 1 个副本写入数据,将停止接受写入。由于复制是异步的,**无法写入**实际上意味着副本已断开连接,或者在超过指定 max-lag
秒数后未向我们发送异步确认。
使用此配置,上述示例中的 Valkey 3 将在 10 秒后变得不可用。当分区恢复时,Sentinel 3 配置将收敛到新配置,并且客户端 B 将能够获取有效配置并继续。
总的来说,Valkey + Sentinel 作为一个整体是**最终一致性系统**,其中合并函数是**最后一次故障转移获胜**,并且旧主服务器的数据被丢弃以复制当前主服务器的数据,因此总是存在丢失已确认写入的窗口。这是由于 Valkey 异步复制以及系统“虚拟”合并函数的丢弃性质所致。请注意,这并不是 Sentinel 本身的限制,如果您使用强一致性复制状态机来编排故障转移,相同的属性仍然适用。只有两种方法可以避免丢失已确认的写入:
- 使用同步复制(以及适当的共识算法来运行复制状态机)。
- 使用最终一致性系统,其中同一对象的不同版本可以合并。
Valkey(与其前身 Redis OSS 一样)目前无法使用上述任何系统,且使用它们目前不在开发目标之内。然而,有一些代理在 Redis OSS 存储之上实现了解决方案“2”,例如 SoundCloud Roshi 或 Netflix Dynomite。
Sentinel 持久状态
Sentinel 状态持久化在 sentinel 配置文件中。例如,每当主服务器接收到或创建(由领导 Sentinel 创建)一个新配置时,该配置都会与配置纪元一起持久化到磁盘上。这意味着可以安全地停止和重新启动 Sentinel 进程。
TILT 模式
Valkey 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 命令,我们可能会得到以下响应:
$ valkey-cli -p 26379
127.0.0.1:26379> info
(Other information from Sentinel server skipped.)
# 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 来替代。然而,目前尚不清楚这是否是一个好的解决方案,因为当前系统可以避免进程被挂起或长时间未被调度器执行的问题。
关于本手册页中使用的“master”和“slave”词语的说明:如果不是为了向后兼容,Valkey 项目不再使用“master”和“slave”这两个词。不幸的是,在这个命令中,这些词是协议的一部分,因此,只有当此 API 自然地被废弃时,我们才能删除这些用法。