ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

WuKongIM 动态节点 Stage 9 冒烟手册:用 wkcli sim 验证加入、激活、迁移与缩容全生命周期

WuKongIM 动态节点 Stage 9 冒烟手册:用 wkcli sim 验证加入、激活、迁移与缩容全生命周期
  • 即时通讯
  • 后端

【免费下载链接】WuKongIM

More than just IM 不只是即时通讯(IM)

项目地址:https://gitcode.com/gh_mirrors/wu/WuKongIM
点击查看免费下载

本文是一份面向 WuKongIM 内部 v2 动态数据节点 Stage 9 生产就绪验证的实操手册,核心场景是在真实 WKProto 流量持续运行的同时,通过 Manager 依次完成节点 4 的种子加入(seed join)、激活、Slot 上移(onboarding)、标记离开、网关排空(drain)、缩容迁移与安全移除,并全程采集状态与指标证据。读完本文,你将掌握wkcli sim压测命令的参数语义与运行机制、动态节点生命周期 8 步操作顺序、健康上报相关配置,以及如何用低基数指标验证“节点在 joining 阶段不可调度、激活且健康新鲜后才可调度”这一关键约束。

背景:这份手册要验证什么

这份可选人工冒烟手册是对test/e2e/cluster/dynamic_node_readiness自动化测试的补充。e2e 测试包在其 AGENTS.md 中明确了 Stage 9 的场景契约:启动一个静态三节点集群,在真实 WKProtoSEND -> SENDACK流量持续运行期间完成节点 4 的加入、激活、上移、缩容与移除,并用公开的 Manager HTTP 接口、公开的/metrics接口和 WKProto 客户端证明健康新鲜度与生命周期阻塞项全过程可解释。

冒烟手册与 e2e 测试共同遵守一条黑盒原则:只使用公开的 v2 bench、Manager、Metrics 和 WKProto 接口面,绝不导入服务端内部实现(不引用internal/app、internal/usecase、存储内部、Controller 内部或集群内部)。这让冒烟结果能真实反映生产可观察性,而不是依赖内部函数。

集群形态:健康上报参数怎么配

冒烟要求按接近生产的形态启动一个 Stage 9 集群:Manager HTTP、bench API、Metrics、网关监听器与健康上报设置全部开启。其中最关键的是节点健康上报的两个参数,通过环境变量注入:

WK_CLUSTER_NODE_HEALTH_REPORT_INTERVAL=5s WK_CLUSTER_NODE_HEALTH_REPORT_TTL=30s

这两个变量在 internal/config/schema.go 中有明确的 TOML 路径映射,等价于wukongim.toml中[cluster]段的配置:

[cluster] node_health_report_interval = "5s" node_health_report_ttl = "30s"

示例配置见仓库根目录 wukongim.toml.example。含义拆解:

  • node_health_report_interval:每个数据节点向集群上报一次健康状态的周期,默认5s。上报越频繁,健康新鲜度判定越敏感。
  • node_health_report_ttl:一次健康上报被视为“新鲜”的有效期,默认30s。超过 TTL 未刷新,节点健康会被判定为stale(过期),从而触发调度与缩容阻塞。

在自动化 e2e 测试中,为了让验证时间可控,readinessOverrides()(见 test/e2e/cluster/dynamic_node_readiness/lifecycle_helpers_test.go)把上报间隔缩短到了500ms:

"WK_CLUSTER_NODE_HEALTH_REPORT_INTERVAL": "500ms", "WK_CLUSTER_NODE_HEALTH_REPORT_TTL": "30s",

手册建议:本地短冒烟时,先把集群保持在 3 个活跃数据节点上,再通过种子加入引入节点 4,避免一开始就形成不完整的拓扑。

压测流量:wkcli sim 的参数与运行机制

在生命周期变更进行期间,通过公开的wkcli sim持续跑真实流量。手册给出的基准命令:

go run ./cmd/wkcli sim --server http://127.0.0.1:5001 --users 200 --groups 40 --group-members 20 --rate 2/s --status-listen 127.0.0.1:19091 --max-runtime 5m

命令定义在 cmd/wkcli/internal/sim/command.go,参数默认值与校验逻辑在 cmd/wkcli/internal/sim/config.go。逐项解读:

参数本冒烟取值默认值说明
--serverhttp://127.0.0.1:5001必填(或--gateway/--context)WuKongIM HTTP API 服务地址,可重复或逗号分隔传入多个
--users200100模拟用户数
--groups4020模拟群组频道数
--group-members2010每个群组的成员数
--rate2/s0.2/s每个群组的消息发送速率,格式如2/s
--status-listen127.0.0.1:19091127.0.0.1:19091本地状态端点监听地址
--max-runtime5m0(不限时)模拟总运行时长上限,0 表示不限制

其余常用参数还包括:--gateway(WKProto TCP 网关地址,覆盖 preflight 探测到的网关)、--payload-size(默认128B,支持1KB、1MiB等)、--connect-rate(默认 20,每秒最大建连数)、--concurrency(默认 64,最大并发模拟操作数)、--ack-timeout(默认 5s,SENDACK 等待超时)、--heartbeat-interval(默认 30s,模拟客户端 PING 间隔)、--json(JSON 输出)。注意--rate是每个群组的速率,总发送速率 =rate × groups。

压测期间不要让 sim 结束:手册明确要求“Keep the simulator running while the operator performs the lifecycle flow”,因为通过标准的每一条都需要在流量不中断的前提下验证。

sim 内部如何工作

wkcli sim的运行时实现位于 cmd/wkcli/internal/sim/runtime.go,其执行脉络可以概括为:

  1. preflight:先向 bench API 探测目标能力并解析网关地址(runtime.go);
  2. setup:通过 v2 bench API 准备拓扑数据(用户、群组、成员);
  3. connect:按--connect-rate限速批量建立 WKProto 客户端连接(frame.APP设备标记);
  4. runTraffic:按总速率调度发送,每个模拟用户最多 32 条在途 SEND(simClientMaxInflight),发送结果以ReasonSuccess计为messages_sent,失败或缺失 SENDACK 计为send_errors;
  5. 重试:运行时状态机依次经历starting → preflighting → setting_up → connecting → running → retrying → stopped,出错后以 2s 固定退避自动重连重跑(见 runtime.go),因此单次网络抖动不会终止整个冒烟。

sim 的实时快照通过本地状态端点暴露。状态服务器实现在 cmd/wkcli/internal/sim/server.go,提供两个 GET 端点:

  • /healthz:返回{"status":"ok"}的存活探针;
  • /status:返回完整 JSON 快照(结构定义见 cmd/wkcli/internal/sim/status.go),字段包括state、run_id、target_servers、gateway_tcp_addrs、users、active_users、groups、group_members、messages_sent、send_errors、recv_messages、recv_dropped、reconnects、last_error、last_transition_at。

这正是手册“Evidence To Keep”中curl http://127.0.0.1:19091/status的数据来源:只要send_errors不持续增长、messages_sent持续增加,就说明生命周期操作期间流量一直在正常推进。

生命周期流程:8 步操作顺序

通过 Manager HTTP 接口或 Manager UI,按以下顺序执行(对应 e2e 测试 production_readiness_test.go 中逐步验证的同一流程):

  1. 种子加入节点 4:通过 seed join 把新节点接入现有 3 节点集群,JoinToken 校验通过后节点进入成员清单;
  2. 等待节点 4 进入joining且健康alive新鲜:此时 Manager 能列出节点,健康上报保持新鲜,但节点不可调度——这是关键约束;
  3. 激活节点 4:激活后等待 join state 变为active,节点变为可调度;
  4. 启动一个有界(bounded)Slot 上移任务到节点 4:把少量 Slot 副本迁往新节点,验证新增容量落地;
  5. 标记节点 4leaving:进入缩容准备阶段;
  6. 开启节点 4 的网关排空(gateway drain):停止新连接接入并排空存量连接;
  7. 推进一个有界的 Slot 缩容迁移,移离节点 4:把 Slot 数据迁往其他存活节点;
  8. 仅在safe_to_remove=true后移除节点 4:最终移除前必须确认所有阻塞项已清空。

e2e 测试中对应的断言顺序值得参考:manager.EventuallyNodeJoinState(t, 4, "joining", ...)→eventuallyNodeSchedulable前的requireNodeNotSchedulable→ 激活后EventuallyNodeJoinState(..., "active")→ onboarding 计划与执行 →leaving→ 缩容排空 →EventuallyScaleInSafeToRemove→removed(见 production_readiness_test.go)。整个流程中每个阶段结束后都调用requireTrafficProgress确认发送仍在推进。

关于safe_to_remove的语义,可以在源码中找到精确定义:缩容目标“没有已知的 control、Slot、Channel 或 runtime 排空阻塞项”时才为真(见 internal/usecase/management/scale_in.go),并通过 Manager 诊断接口以 JSON 字段safe_to_remove暴露(见 internal/access/manager/dynamic_node_diagnostics.go)。

证据收集:要保留哪些数据

手册要求冒烟全程保留以下证据,供事后审计与复现:

  • curl http://127.0.0.1:19091/status:sim 运行快照,证明流量持续推进;
  • Manager 节点列表 JSON:每个生命周期转换前后各一份,对比 join state 与健康状态;
  • Manager 缩容状态 JSON:最终移除前获取,确认safe_to_remove;
  • /metrics片段:重点采集以下 5 个指标。

这 5 个指标的完整定义与标签约束在 pkg/metrics/node_lifecycle.go 中实现:

指标类型标签含义
wukongim_node_lifecycle_nodesGaugeVecjoin_state,status按持久化 join 状态与健康状态分组的当前节点数
wukongim_node_health_freshness_nodesGaugeVecfreshness,status按健康新鲜度(fresh/stale/missing)与状态分组的节点数
wukongim_node_lifecycle_attempts_totalCounterVecoperation,result生命周期操作尝试次数(join/activate/mark_leaving/onboarding/scale_in等 ×ok/fail/conflict等)
wukongim_node_scale_in_blockers_totalCounterVecreason缩容阻塞原因计数(如target_health_missing、target_health_stale、target_runtime_not_ready等 9 种有界原因)
wukongim_discovery_membership_revisionGauge无本地可见的成员版本号,用于确认成员变更已收敛

除此之外,同一文件还暴露了wukongim_node_health_report_age_seconds(健康上报最大年龄)、wukongim_node_onboarding_tasks(上移任务数)以及wukongim_slot_replica_move_*系列(Slot 副本迁移时长/失败/阶段观测),可用于更细粒度地审计上移与缩容迁移过程。

低基数设计是这套指标的核心原则:所有标签值都经过normalizeBoundedMetricLabel归一化(node_lifecycle.go),不在白名单内的值统一折叠为other/unknown。例如 join state 只允许active/joining/leaving/removed,健康状态只允许alive/suspect/down,操作只允许固定的 9 种。这避免了高基数标签拖垮 Prometheus,也让仪表盘聚合稳定可靠。e2e 测试的指标断言(metrics_helpers_test.go)会轮询直到目标标签组合出现,并校验最小值,例如wukongim_node_lifecycle_nodes{join_state="active",status="alive"} >= 3。

通过标准:如何判定冒烟成功

逐条对照手册的 Pass Criteria:

  1. wkcli sim在 join、激活、上移、缩容与移除全程持续报告发送成功——用/status的messages_sent增量与send_errors收敛来判定;
  2. 节点 4 仅在joining时绝不可调度——通过 Manager 节点列表或调度相关状态确认;
  3. 节点 4 仅在激活且健康alive新鲜后才可调度——健康新鲜度需同时满足fresh == true、freshness == "fresh"、status == "alive"、RuntimeReady(见 lifecycle_helpers_test.go 的健康轮询条件);
  4. 最终移除仅在 Slot、Channel、健康与网关排空阻塞项全部清空后才尝试——对应safe_to_remove=true,并由wukongim_node_scale_in_blockers_total的各个 reason 标签给出可解释证据;
  5. 指标以低基数标签暴露生命周期与健康证据——即上述全部指标均遵守有界标签白名单。

一键脚本与自动化对照

除了手工冒烟,仓库还提供了可直接运行的冒烟脚本 scripts/smoke-wkcli-sim-wukongim-three-nodes.sh,它启动本地三节点集群、跑一轮真实wkcli sim流量并采集证据。其中与动态节点生命周期相关的关键选项:

  • --auto-join-node:在 sim 发送阶段启动一个 seed join 数据节点(默认节点 4),配合--auto-join-after、--auto-join-seeds、--auto-join-token使用;
  • --auto-promote-controller-voter:新节点就绪后通过 Manager API(POST /manager/nodes/{id}/activate、/controller-voter/promote)激活并提升为 Controller voter;
  • --fault-kill-node:在发送阶段杀掉一个静态节点,配合--fault-health-report-interval/--fault-health-report-ttl覆盖健康上报参数,用于故障演练对照。

自动化 e2e 版本可在仓库根目录按 AGENTS.md 的说明串行运行:

GOWORK=off go test -tags=e2e ./test/e2e/cluster/dynamic_node_readiness -count=1 -timeout 12m -p=1

该包必须用-p=1串行执行,任务扇出保持有界(通常max_slot_moves=1),并优先轮询公开状态而不是固定 sleep,这些约束同样适用于手工冒烟:一次只做一个有界迁移,让证据链每一步都可解释。

小结

这份 Stage 9 冒烟手册的核心价值在于:用公开接口把“动态节点加入/缩容不影响在线业务”这一生产要求变成可重复、可取证、可观测的流程。实操要点可归纳为四件事:把健康上报间隔与 TTL 配成可观测的节奏(5s/30s,e2e 用 500ms 加速)、让wkcli sim流量贯穿全程并通过/status持续观察、严格按 8 步顺序执行生命周期操作并在每个转换点留证、最后用 5 个低基数指标(外加 Slot 迁移与上移任务系列)作为通过判据。如需深入底层实现,可继续阅读 pkg/metrics/node_lifecycle.go(指标与标签约束)、cmd/wkcli/internal/sim/runtime.go(sim 运行时与重试机制)、test/e2e/cluster/dynamic_node_readiness/production_readiness_test.go(完整生命周期断言)。

  • 即时通讯
  • 后端

【免费下载链接】WuKongIM

More than just IM 不只是即时通讯(IM)

项目地址:https://gitcode.com/gh_mirrors/wu/WuKongIM
点击查看免费下载

相关推荐

上一篇:突破分布式瓶颈:Apache ZooKeeper队列性能极限测试与调优指南
下一篇:ChatAgent 深度指南:用 openJiuwen 最简对话智能体打通提示词调优链路

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进