跳转到主要内容

支持 KeyDB Sentinel 的 KeyDB 客户端指南

KeyDB Sentinel 是一个针对 KeyDB 实例的监控解决方案,它能处理 KeyDB 主节点的自动故障转移和服务发现(谁是给定实例组的当前主节点?)。由于 Sentinel 负责在故障转移期间重新配置实例,并向连接到 KeyDB 主节点或副本的客户端提供配置,因此客户端需要明确支持 KeyDB Sentinel。

本文档面向希望在其客户端实现中支持 Sentinel 的 KeyDB 客户端开发者,旨在实现以下目标:

  • 通过 Sentinel 自动配置客户端。
  • 提高 KeyDB Sentinel 自动故障转移的安全性。

有关 KeyDB Sentinel 工作原理的详细信息,请查阅 KeyDB 文档,因为本文档仅包含 KeyDB 客户端开发者所需的信息,并假定读者熟悉 KeyDB Sentinel 的工作方式。

通过 Sentinel 进行 KeyDB 服务发现#

KeyDB Sentinel 使用类似 "stats" 或 "cache" 这样的名称来标识每个主节点。每个名称实际上标识了一个*实例组*,由一个主节点和数量不等的副本组成。

在网络中用于特定目的的 KeyDB 主节点地址可能会因自动故障转移、手动触发的故障转移(例如为了升级 KeyDB 实例)以及其他原因而发生变化。

通常,KeyDB 客户端会有一些硬编码的配置,以 IP 地址和端口号的形式指定网络中 KeyDB 主实例的地址。然而,如果主节点地址发生变化,就需要对每个客户端进行手动干预。

支持 Sentinel 的 KeyDB 客户端可以利用 KeyDB Sentinel 从主节点名称自动发现 KeyDB 主节点的地址。因此,支持 Sentinel 的客户端除了使用硬编码的 IP 地址和端口外,还应能够选择性地接受以下输入:

  • 一个指向已知 Sentinel 实例的 ip:port 对列表。
  • 服务名称,如 "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:port 对。
  • 一个空回复。这意味着 Sentinel 不知道这个主节点。

如果收到一个 ip:port 对,则应使用此地址连接到 KeyDB 主节点。否则,如果收到空回复,客户端应尝试列表中的下一个 Sentinel。

步骤 3:在目标实例中调用 ROLE 命令#

一旦客户端发现了主实例的地址,它应该尝试与主节点建立连接,并调用 ROLE 命令来验证该实例的角色确实是主节点。

如果 ROLE 命令不可用(它是在 KeyDB 2.8.12 中引入的),客户端可以转而使用 INFO replication 命令,并解析其输出中的 role: 字段。

如果实例并非预期的主节点,客户端应等待一小段时间(几百毫秒),然后从步骤 1 开始重试。

处理重新连接#

一旦服务名称被解析为主节点地址并与 KeyDB 主实例建立连接后,每当需要重新连接时,客户端都应使用 Sentinel 从步骤 1 开始重新解析地址。例如,在以下情况下应再次联系 Sentinel:

  • 如果客户端在超时或套接字错误后重新连接。
  • 如果客户端因为被用户明确关闭或重新连接而重新连接。

在上述情况以及任何其他客户端与 KeyDB 服务器失去连接的情况下,客户端都应重新解析主节点地址。

Sentinel 故障转移断开连接#

当 KeyDB Sentinel 更改一个实例的配置时,例如将一个副本提升为主节点,在故障转移后将一个主节点降级为新主节点的副本,或者只是更改一个过时副本实例的主节点地址,它会向该实例发送一个 CLIENT KILL type normal 命令,以确保所有客户端都与被重新配置的实例断开连接。这将强制客户端重新解析主节点地址。

如果客户端联系到一个信息尚未更新的 Sentinel,通过 ROLE 命令对 KeyDB 实例角色的验证将会失败,这使客户端能够检测到所联系的 Sentinel 提供了过时的信息,并会再次尝试。

注意:有可能一个过时的主节点在客户端联系一个过时的 Sentinel 实例的同时恢复在线,因此客户端可能会连接到一个过时的主节点,而 ROLE 的输出仍然匹配。然而,当该主节点再次上线时,Sentinel 会尝试将其降级为副本,从而触发新的断开连接。同样的逻辑也适用于连接到将被重新配置为与不同主节点进行复制的过时副本。

连接到副本#

有时客户端有兴趣连接到副本,例如为了扩展读取请求。该协议通过对步骤 2 进行轻微修改来支持连接到副本。客户端不应调用以下命令:

SENTINEL get-master-addr-by-name master-name

客户端应改为调用:

SENTINEL replicas master-name

以获取副本实例的列表。

同样地,客户端应使用 ROLE 命令验证该实例确实是一个副本,以避免将读取查询扩展到主节点。

连接池#

对于实现连接池的客户端,在单个连接重新连接时,应再次联系 Sentinel,并且在主节点地址发生变化的情况下,应关闭所有现有连接并连接到新地址。

错误报告#

客户端应在出现错误时正确地向用户返回信息。具体来说:

  • 如果无法联系到任何 Sentinel(因此客户端从未能获得对 SENTINEL get-master-addr-by-name 的回复),则应返回一个明确指出 KeyDB Sentinel 不可达的错误。
  • 如果池中的所有 Sentinel 都回复了空回复,则应通过一个错误告知用户 Sentinel 不知道此主节点名称。

Sentinel 列表自动刷新#

可选地,一旦收到对 get-master-addr-by-name 的成功回复,客户端可以按照以下过程更新其内部的 Sentinel 节点列表:

  • 使用命令 SENTINEL sentinels <master-name> 获取此主节点的其他 Sentinel 列表。
  • 将列表中尚不存在的每个 ip:port 对添加到列表的末尾。

客户端不需要能够通过更新自己的配置来使列表持久化。能够升级 Sentinel 列表的内存表示已经足以提高可靠性。

订阅 Sentinel 事件以提高响应能力#

Sentinel 文档展示了客户端如何使用 Pub/Sub 连接到 Sentinel 实例,以订阅 KeyDB 实例配置的更改。

此机制可用于加速客户端的重新配置,即客户端可以监听 Pub/Sub 以了解配置何时发生变化,从而运行本文档中解释的三个步骤协议来解析新的 KeyDB 主节点(或副本)地址。

然而,通过 Pub/Sub 收到的更新消息不应取代上述过程,因为无法保证客户端能够接收到所有更新消息。