Valkey Sentinel 是一个 Valkey 实例的监控解决方案,负责处理 Valkey 主节点的自动故障转移和服务发现(某个给定实例组的当前主节点是谁?)。由于 Sentinel 既负责在故障转移期间重新配置实例,也负责为连接到 Valkey 主节点或副本的客户端提供配置,因此客户端需要明确支持 Valkey Sentinel。
本文档面向希望在其客户端实现中支持 Sentinel 的 Valkey 客户端开发者,旨在达到以下目标:
- 通过 Sentinel 自动配置客户端。
- 提高 Valkey Sentinel 自动故障转移的安全性。
有关 Valkey Sentinel 工作原理的详细信息,请查阅Valkey 文档,因为本文档仅包含 Valkey 客户端开发者所需的信息,并假定读者熟悉 Valkey Sentinel 的工作方式。
通过 Sentinel 进行 Valkey 服务发现
Valkey Sentinel 通过诸如“stats”或“cache”之类的名称来标识每个主节点。每个名称实际上标识了一个实例组,该组由一个主节点和数量可变的副本组成。
在网络内部用于特定目的的 Valkey 主节点地址可能会因自动故障转移、手动触发故障转移(例如为了升级 Valkey 实例)以及其他原因而改变。
通常,Valkey 客户端具有某种硬编码配置,将网络中的 Valkey 主实例地址指定为 IP 地址和端口号。然而,如果主地址发生变化,则需要对每个客户端进行手动干预。
支持 Sentinel 的 Valkey 客户端可以使用 Valkey Sentinel 从主节点名称自动发现 Valkey 主节点的地址。因此,支持 Sentinel 的客户端应该能够(可选地)接收以下输入,而不是硬编码的 IP 地址和端口:
- 指向已知 Sentinel 实例的 IP:端口 对列表。
- 服务的名称,例如“cache”或“timelines”。
这是客户端为了从 Sentinel 列表和服务名称开始获取主地址应遵循的程序。
步骤 1:连接到第一个 Sentinel
客户端应该遍历 Sentinel 地址列表。对于每个地址,它应该尝试连接到 Sentinel,并使用一个短超时(几百毫秒的数量级)。如果出现错误或超时,则应尝试下一个 Sentinel 地址。
如果所有 Sentinel 地址都尝试失败,则应向客户端返回错误。
第一个响应客户端请求的 Sentinel 应该被放在列表的开头,以便在下次重新连接时,我们首先尝试在上次连接尝试中可达的 Sentinel,从而最大限度地减少延迟。
步骤 2:请求主地址
一旦与 Sentinel 建立连接,客户端应重试在 Sentinel 上执行以下命令:
SENTINEL get-master-addr-by-name master-name
其中 master-name 应替换为用户指定的实际服务名称。
此调用的结果可以是以下两种回复之一:
- 一个 IP:端口 对。
- 一个空回复。这意味着 Sentinel 不知道这个主节点。
如果收到一个 IP:端口 对,则应使用此地址连接到 Valkey 主节点。否则,如果收到空回复,客户端应尝试列表中的下一个 Sentinel。
步骤 3:在目标实例中调用 ROLE 命令
一旦客户端发现了主实例的地址,它应该尝试与主节点建立连接,并调用 ROLE
命令以验证实例的角色确实是主节点。
如果实例未如预期般是主节点,客户端应等待一小段时间(几百毫秒),然后应从步骤 1 开始再次尝试。
处理重新连接
一旦服务名称解析为主地址并与 Valkey 主实例建立连接,每次需要重新连接时,客户端都应从步骤 1 开始,使用 Sentinel 重新解析地址。例如,在以下情况下应再次联系 Sentinel:
- 如果客户端在超时或套接字错误后重新连接。
- 如果客户端因用户明确关闭或重新连接而重新连接。
在上述情况以及客户端与 Valkey 服务器失去连接的任何其他情况下,客户端都应再次解析主地址。
Sentinel 故障转移断开连接
当 Valkey Sentinel 更改实例的配置时,例如将副本提升为主节点,在故障转移后将主节点降级为复制到新的主节点,或简单地更改过时副本实例的主地址时,它会向实例发送一个 CLIENT KILL type normal
命令,以确保所有客户端都从重新配置的实例断开连接。这将强制客户端再次解析主地址。
如果客户端联系的 Sentinel 信息尚未更新,通过 ROLE
命令验证 Valkey 实例角色将失败,从而使客户端能够检测到所联系的 Sentinel 提供了过时信息,并会再次尝试。
注意:有可能一个过时的主节点在客户端联系一个过时的 Sentinel 实例的同时重新上线,因此客户端可能会连接到一个过时的主节点,而 ROLE 输出却匹配。然而,当该主节点再次恢复时,Sentinel 会尝试将其降级为副本,从而触发新的断开连接。同样的逻辑也适用于连接到将被重新配置以复制到不同主节点的过时副本。
连接到副本
有时客户端有兴趣连接到副本,例如为了扩展读请求。本协议通过稍微修改步骤 2 来支持连接到副本。客户端不应调用以下命令:
SENTINEL get-master-addr-by-name master-name
客户端应改为调用:
SENTINEL replicas primary-name
以获取副本实例列表。
同样地,客户端应使用 ROLE
命令验证实例是否确实是副本,以避免与主节点一起扩展读查询。
连接池
对于实现连接池的客户端,在单个连接重新连接时,应再次联系 Sentinel,并且如果主地址发生变化,所有现有连接都应关闭并连接到新地址。
错误报告
客户端应在出现错误时正确地向用户返回信息。具体而言:
- 如果无法联系到任何 Sentinel(以至于客户端从未能获得
SENTINEL get-master-addr-by-name
的回复),则应返回一个明确指出 Valkey Sentinel 不可达的错误。 - 如果池中所有 Sentinel 都返回了空回复,则应向用户提示一个错误,说明 Sentinel 不知道此主节点名称。
Sentinel 列表自动刷新
可选地,一旦收到对 get-master-addr-by-name
的成功回复,客户端可以按照此程序更新其内部 Sentinel 节点列表:
- 使用命令
SENTINEL sentinels <master-name>
获取此主节点的其他 Sentinel 列表。 - 将列表中尚未存在的每个 IP:端口 对添加到列表末尾。
客户端不需要能够通过更新其自身配置来使列表持久化。能够升级 Sentinel 列表的内存表示已经有助于提高可靠性。
订阅 Sentinel 事件以提高响应性
Sentinel 文档展示了客户端如何使用 Pub/Sub 连接到 Sentinel 实例,以订阅 Valkey 实例配置的变化。
此机制可用于加速客户端的重新配置,即客户端可以监听 Pub/Sub 以了解何时发生了配置更改,从而运行本文档中解释的三步协议来解析新的 Valkey 主节点(或副本)地址。
然而,通过 Pub/Sub 接收到的更新消息不应替代上述程序,因为无法保证客户端能够接收到所有更新消息。
注意:若非为了向后兼容,Valkey 项目不再使用“master”和“slave”这两个词。不幸的是,在给定的命令中,这些词是协议的一部分,所以我们只有在这些 API 自然弃用时才能移除此类用法。