Dragonfly 集群节点健康状态(Node Health)机制全解析:配置、命令行为与源码实现
【免费下载链接】dragonflyA modern replacement for Redis and Memcached项目地址: https://gitcode.com/GitHub_Trending/dr/dragonfly
节点健康状态(Node Health)是 Dragonfly 集群控制面(cluster manager / orchestrator)通过DFLYCLUSTER CONFIG命令下发给各节点的被动元数据:节点本身不主动探测自身状态,而是由集群管理器负责监控,并把每个节点的健康状态写进集群配置 JSON 中。本文围绕 docs/cluster-node-health.md 展开,完整讲解online / loading / fail / hidden四种健康状态的定义、配置格式、CLUSTER SHARDS / CLUSTER SLOTS / CLUSTER NODES三个命令的过滤与展示行为,并深入到 src/server/cluster 源码层验证其实现细节,帮助你正确管理集群节点的上下线与可见性,实现 Valkey 兼容的集群客户端行为。
概述:节点健康状态是什么
Dragonfly 对集群节点健康状态的支持,是为了让集群管理器能够跟踪每个节点的健康状态,并通过各类集群命令把该状态传达给客户端。该特性在 PR #4758 与 PR #4767 中引入,用于解决 issue #4741 中提出的集群节点健康管理需求。
核心设计理念是:节点健康是被动元数据,由控制面提供。节点不会自行判定"我是否健康",而是由集群编排器监控节点状态,再通过集群配置(DFLYCLUSTER CONFIG)把健康信息同步给每个节点。这意味着:
- 健康状态是集群配置的一部分,master 与 replica 节点都可以设置;
- 不同集群命令会基于健康状态对节点做过滤或展示处理;
- 状态变更的判定逻辑(如节点是否失联、是否仍在同步)由集群管理器负责,Dragonfly 只负责"接收并表达"。
四种健康状态
Dragonfly 为集群节点定义了四种健康状态,定义于 src/server/cluster/cluster_defs.h:
enum class NodeHealth : std::uint8_t { FAIL, LOADING, ONLINE, HIDDEN };| 状态 | 含义 | 在命令中的可见性 |
|---|---|---|
online | 节点完全可用,随时可以服务请求 | 所有命令 |
loading | 节点仍在加载数据(例如初次同步或重启过程中) | CLUSTER SHARDS、CLUSTER NODES |
fail | 节点已失败或不可达 | CLUSTER SHARDS、CLUSTER NODES |
hidden | replica 存在但不应暴露给客户端(供集群管理器内部使用) | master:所有命令;replica:均不可见 |
默认状态
当配置中未指定健康状态时,节点默认处于online状态。该默认值定义在ClusterExtendedNodeInfo结构体中(见 src/server/cluster/cluster_defs.h):
struct ClusterExtendedNodeInfo : ClusterNodeInfo { NodeHealth health = NodeHealth::ONLINE; ... };从源码结构可以推断:ClusterExtendedNodeInfo在基础节点信息(id、ip、port,见 src/server/cluster/cluster_defs.h)之上扩展了health字段,并内置了ONLINE默认值,因此省略health字段的节点配置天然就是online。
配置:如何设置节点健康状态
节点健康状态通过DFLYCLUSTER CONFIG命令传入的集群配置 JSON 进行设置,每个节点使用health字段声明状态。
配置格式
[ { "slot_ranges": [ { "start": 0, "end": 16383 } ], "master": { "id": "node-master-1", "ip": "10.0.0.1", "port": 7000, "health": "online" }, "replicas": [ { "id": "node-replica-1", "ip": "10.0.0.2", "port": 7001, "health": "online" }, { "id": "node-replica-2", "ip": "10.0.0.3", "port": 7002, "health": "loading" }, { "id": "node-replica-3", "ip": "10.0.0.4", "port": 7003, "health": "fail" }, { "id": "node-replica-4", "ip": "10.0.0.5", "port": 7004, "health": "hidden" } ] } ]下发配置
使用DFLYCLUSTER CONFIG命令下发包含健康信息的集群配置:
DFLYCLUSTER CONFIG <json_config>health字段可选且大小写不敏感,合法取值为:online、loading、fail、hidden。从命令注册信息看(src/server/cluster/cluster_family.cc),DFLYCLUSTER属于CO::ADMIN管理类命令,需要集群管理权限(acl::kDflyCluster)才能执行。
解析逻辑的源码细节
health字段在 src/server/cluster/cluster_config.cc 的ParseClusterNode函数中被解析:
- 依次解析
id、ip、port,任一项非法都会导致该节点解析失败并返回nullopt; health通过absl::EqualsIgnoreCase做大小写不敏感匹配,"FAIL"、"LOADING"、"ONLINE"、"HIDDEN"四种写法(任意大小写组合)均可识别;- 若
health存在但不是字符串、或取值不合法,会记录LOG(ERROR)错误日志,但不会使整体配置解析失败——该节点保持默认的ONLINE状态; - master 与 replicas 都经由同一个
ParseClusterNode解析(见 src/server/cluster/cluster_config.cc 中BuildClusterConfigFromJson的调用),所以两种角色的节点都支持health字段。
单测 src/server/cluster/cluster_config_test.cc 中的TEST_F(ClusterConfigTest, NodesHealth)直接验证了该解析行为:配置中 master 为online、两个 replica 分别为loading与fail,解析后断言master.health == NodeHealth::ONLINE、replicas.front().health == NodeHealth::LOADING、replicas.back().health == NodeHealth::FAIL。
命令行为:不同命令如何对待健康状态
不同集群命令对节点健康状态的处理方式不同,核心逻辑集中在 src/server/cluster/cluster_family.cc 中。
CLUSTER SHARDS
CLUSTER SHARDS返回集群分片的详细信息,包含除hidden之外所有节点的健康状态。
示例输出:
127.0.0.1:6379> CLUSTER SHARDS 1) 1) "slots" 2) 1) (integer) 0 2) (integer) 16383 3) "nodes" 4) 1) 1) "id" 2) "node-master-1" 3) "endpoint" 4) "10.0.0.1" 5) "ip" 6) "10.0.0.1" 7) "port" 8) (integer) 7000 9) "role" 10) "master" 11) "replication-offset" 12) (integer) 0 13) "health" 14) "online" 2) 1) "id" 2) "node-replica-1" 3) "endpoint" 4) "10.0.0.2" 5) "ip" 6) "10.0.0.2" 7) "port" 8) (integer) 7001 9) "role" 10) "replica" 11) "replication-offset" 12) (integer) 0 13) "health" 14) "online" 3) 1) "id" 2) "node-replica-2" 3) "endpoint" 4) "10.0.0.3" 5) "ip" 6) "10.0.0.3" 7) "port" 8) (integer) 7002 9) "role" 10) "replica" 11) "replication-offset" 12) (integer) 0 13) "health" 14) "loading" 4) 1) "id" 2) "node-replica-3" 3) "endpoint" 4) "10.0.0.4" 5) "ip" 6) "10.0.0.4" 7) "port" 8) (integer) 7003 9) "role" 10) "replica" 11) "replication-offset" 12) (integer) 0 13) "health" 14) "fail"注意:hidden状态的节点会被过滤掉,不出现在输出中。
源码依据(src/server/cluster/cluster_family.cc):ClusterFamily::ClusterShards先通过std::erase_if将health == NodeHealth::HIDDEN的 replica 从分片信息中剔除,再调用ClusterShardsImpl输出。每个节点输出 14 个字段(id、endpoint、ip、port、role、replication-offset、health),其中health字段直接使用ToString(node.health)序列化(src/server/cluster/cluster_defs.cc 中的ToString将枚举映射为"fail"/"loading"/"online"/"hidden"小写字符串)。过滤只作用于 replica——即使 master 被标记为HIDDEN,它仍然会出现在输出中。
CLUSTER SLOTS
CLUSTER SLOTS返回槽位分布信息,会过滤掉尚未就绪、无法服务请求的 replica。
过滤行为:
- 包含
online状态的 replica; - 排除
loading、fail、hidden状态的 replica。
示例输出:
127.0.0.1:6379> CLUSTER SLOTS 1) 1) (integer) 0 2) (integer) 16383 3) 1) "10.0.0.1" 2) (integer) 7000 3) "node-master-1" 4) 1) "10.0.0.2" 2) (integer) 7001 3) "node-replica-1"本例中,只有 master 和online的 replica(node-replica-1)被展示;loading、fail、hidden状态的 replica 均不包含在内。
源码依据(src/server/cluster/cluster_family.cc):ClusterSlotsImpl用std::erase_if一次剔除三种非就绪状态的 replica——HIDDEN、FAIL、LOADING(源码注释为 "we need to remove hidden and fail replicas")。剔除逻辑同样只作用于 replica 列表,master 始终被包含。注意该命令的响应中每个节点只输出ip、port、id三个字段,不包含health字段。
CLUSTER NODES
CLUSTER NODES以空格分隔的文本格式返回全部集群节点列表,展示大多数健康状态的节点,但排除hidden节点。
连接状态映射:
online与loading节点:显示为connectedfail节点:显示为disconnectedhidden节点:不显示在输出中
示例输出:
127.0.0.1:6379> CLUSTER NODES node-master-1 10.0.0.1:7000@7000 master - 0 0 0 connected 0-16383 node-replica-1 10.0.0.2:7001@7001 slave node-master-1 0 0 0 connected node-replica-2 10.0.0.3:7002@7002 slave node-master-1 0 0 0 connected node-replica-3 10.0.0.4:7003@7003 slave node-master-1 0 0 0 disconnected对照说明:
node-replica-1(online):显示为connectednode-replica-2(loading):显示为connectednode-replica-3(fail):显示为disconnectednode-replica-4(hidden):不显示在输出中
源码依据(src/server/cluster/cluster_family.cc):ClusterNodesImpl的WriteNode用三元表达式完成连接状态映射:
node.health != NodeHealth::FAIL ? "0 0 0 connected" : "0 0 0 disconnected"即只有FAIL状态显示为disconnected,其余(含LOADING)均为connected。列出 replica 时同样用if (replica.health != NodeHealth::HIDDEN)跳过hiddenreplica,而 master 无条件写入(即便被标记为HIDDEN)。输出行之间仅用\n分隔(不使用\r\n),这是 issue #2726 中确定的行为。
典型使用场景
1. 渐进式节点加入(Gradual Node Addition)
当向集群添加新 replica 时,可在其同步数据期间将健康状态设置为loading。这样集群管理器可以跟踪到该节点的存在,但客户端无法通过CLUSTER SLOTS把读请求重定向到它——因为CLUSTER SLOTS只返回online的 replica。同步完成后,集群管理器再把状态更新为online,节点即可对外服务。
2. 故障节点处理(Failed Node Handling)
当节点失败或不可达时,集群管理器可将其标记为fail。这样该节点在CLUSTER SHARDS与CLUSTER NODES中仍然可见(便于运维排查),但会被排除在CLUSTER SLOTS响应之外,客户端不会把请求路由到故障节点。
3. 内部 replica(Internal Replicas)
hidden状态适用于由集群编排器内部管理、但不应暴露给外部客户端的 replica。被标记为hidden的 replica 会从所有集群命令(CLUSTER SHARDS、CLUSTER SLOTS、CLUSTER NODES)中过滤掉。注意:被标记为hidden的 master 在所有命令中仍然可见——过滤只作用于 replica。
4. Valkey 兼容性
该特性提供了 Valkey 兼容的集群客户端 API 行为:
CLUSTER SHARDS返回 replica 节点的健康状态(health字段);CLUSTER SLOTS不返回尚未完成加载(loading等非online)的 replica。
实现细节:从数据模型到命令处理
对于关注实现的开发者,可沿以下链路阅读源码:
数据模型:
NodeHealth枚举定义于 src/server/cluster/cluster_defs.h,包含FAIL、LOADING、ONLINE、HIDDEN四个值;枚举到字符串的映射ToString位于 src/server/cluster/cluster_defs.cc。值得注意:HIDDEN在ToString中带有DCHECK(false)断言("shouldn't be used"),因为hidden节点在输出前已被过滤,正常流程中不应被序列化到任何命令响应。配置解析:健康状态在 src/server/cluster/cluster_config.cc 的
ParseClusterNode函数中从 JSON 解析,master 与 replica 共用该函数;非法health值只记录错误日志、不中断整体解析。命令处理器:src/server/cluster/cluster_family.cc 中三个命令各自实现基于健康状态的过滤逻辑:
ClusterShards(L232-L243):调用ClusterShardsImpl前先剔除HIDDENreplica(master 即使为HIDDEN也保留);ClusterSlotsImpl(L246-L286):剔除HIDDEN、FAIL、LOADING的 replica(master 始终包含);ClusterNodesImpl(L298-L343):列出 replica 时剔除HIDDEN(master 为HIDDEN仍包含),并把健康状态映射为connected/disconnected。
默认值:未指定时节点默认
ONLINE,定义于 src/server/cluster/cluster_defs.h 的ClusterExtendedNodeInfo。测试佐证:src/server/cluster/cluster_family_test.cc 中构造了包含
online/loading/fail/hidden四种 replica 的完整集群配置,并断言CLUSTER SHARDS输出中health字段的取值与hiddenreplica 的缺席;src/server/cluster/cluster_config_test.cc 的NodesHealth用例则验证了 JSON 到枚举的解析结果。
延伸阅读
- Dragonfly 集群模式完整文档
- 集群拓扑相关设计图
- 槽位迁移机制
- 集群节点健康状态原始文档
- 集群配置解析与测试:src/server/cluster/cluster_config.cc、src/server/cluster/cluster_config_test.cc
- 集群命令实现与测试:src/server/cluster/cluster_family.cc、src/server/cluster/cluster_family_test.cc
【免费下载链接】dragonflyA modern replacement for Redis and Memcached项目地址: https://gitcode.com/GitHub_Trending/dr/dragonfly
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考