Skip to content

运行时配置热重载

状态:Current

最后核对:2026-05-16

本文档说明 nexus-backendnexus-agent 当前使用的运行时配置模型。它是 Agent 热重载、配置版本、应用回报和端口冲突处理的设计参考。

摘要

运行时配置通过数据库权威的 RuntimeConfigSnapshot 下发。这个快照是单个节点 运行时目标的聚合根:

  • PingoraConfig:L4 gateway listener、route、upstream、TLS 材料和 listener policy。
  • L3TunnelConfig:L3 tunnel rule 和 listener policy。
  • NodeConfig:Agent 拥有的节点连接设置,目前是 multiplex 开关和端口。

生产 secure-control 路径每个配置代际发送一个快照。Agent 将整个快照作为一个 目标应用,并通过 ConfigApplyReport 回报结果。

protobuf 里仍保留旧的 split ConfigUpdate payload 用于兼容,但生产发布和重放 路径使用 RuntimeConfigSnapshot

已修复的问题

旧热重载逻辑存在以下失败模式:

  • 配置代际语义不足。部分路径发送固定版本,部分手工 push 路径会制造脱离数据库状态的版本。
  • L4、L3 和节点连接设置作为独立局部更新流式下发,stream lag 或局部失败会让 Agent 处于混合运行时目标。
  • broadcast receiver lag 时只继续读取,可能跳过最新权威状态。
  • Agent apply 可能在后续校验或 L3 阶段失败前已经改变部分 listener。
  • L3 tunnel 更新和 gateway L3 QUIC ingress 更新可能分裂。
  • NodeConfig 不在重连重放中,multiplex 变更可能丢失。
  • 后端没有 Agent 明确的 apply ack/nack。
  • 外部端口冲突只记录日志并跳过,导致缺失 listener 的配置看起来像应用成功。

快照契约

RuntimeConfigSnapshot 是权威状态。如果某个配置域没有期望的运行时对象,快照也 必须携带该域的空配置。没有 rule 表示“删除这些运行时对象”,不是“保留旧对象”。

快照必须包含:

  • config_version
  • node_id
  • pingora
  • l3_tunnels
  • node_config

后端把快照放入 ConfigUpdate.runtime_snapshot;外层 ConfigUpdate.config_version 与快照版本保持一致,用于兼容。

版本语义

config_version 来自持久化配置状态。它是一个微秒时间戳代际,取目标节点相关 配置更新时间的最大值:

  • nodes.config_updated_at
  • 引用该节点 ID 或名称的 tunnel chain。
  • 该节点的 listener policy。
  • 包含该节点的 L3 network。
  • 包含该节点的 peer network 中的 L3 endpoint。
  • 包含该节点的 peer network 中的 L3 route。

nodes.updated_at 不作为配置代际来源。heartbeat、hello、metrics、运行时 listener 上报和 ConfigApplyReport 可以更新节点运行态 metadata,但不能制造新的配置代际。

只有明确要发布运行时配置的后端路径会调用 AppState::publish_runtime_config(node_id)。 该方法推进 nodes.config_updated_at,并发送当前数据库权威快照。

发布路径

以下后端路径会发布快照:

  • 端口策略 upsert。
  • L3 network create/update/deploy/delete。
  • L4 chain create/update/deploy/delete。
  • 节点 multiplex 设置变更。
  • POST /api/nodes/{id}/config 刷新。

tunnel chain update 会向旧 transit 节点和新 transit 节点的并集发布快照。这样当某个 节点从 chain 中移除时,该节点也会收到新的空快照或缩减快照,从而删除旧 listener。

tunnel chain delete 会先解析受影响的 transit 节点,再删除数据库行,删除后发布快照。

POST /api/nodes/{id}/config 现在是刷新接口。它接受空 body 或 {},发送当前 数据库权威快照。内联 tcp_rulesupstreams payload 会被拒绝,因为它们会绕过 持久化配置代际模型。

重放和 Lag 处理

Agent 在 secure-control ConfigRequest 中发送最后已应用的 config_version。 后端为该节点重新构建当前快照,仅当当前版本更新时发送。

如果后端 broadcast receiver lag,不信任中间遗漏消息,而是重新构建并发送当前权威快照。

历史 AgentService.StreamConfig 入口也遵循同样的快照重放形态,但生产 Agent 控制 路径是 SecureAgentService.Control

Agent 应用流程

Agent 按以下顺序应用快照:

  1. 拒绝其他 node_id 的快照。
  2. 忽略 config_version <= last_config_version 的旧快照。
  3. 校验快照包含 PingoraConfigL3TunnelConfig
  4. 准备 L3 tunnel 状态,但不修改 L3 运行时。
  5. 应用合并后的 gateway 配置,包括 L4 和 L3 QUIC ingress。
  6. 提交准备好的 L3 tunnel 状态。
  7. 应用 NodeConfig multiplex 设置。
  8. 标记版本已应用,并在后续 ConfigRequest 中携带该版本。
  9. 发送 ConfigApplyReport

Agent 仍保留旧 split update 处理用于兼容,但生产行为是 snapshot 处理。

回滚规则

快照应用不能让 Agent 留在半新状态。

Agent 在应用新快照前保存旧 gateway、L3 和 multiplex 运行时状态。如果 gateway 校验或 listener reconcile 失败,gateway 内部恢复旧状态。如果 gateway 已应用但 L3 commit 失败,则恢复旧 gateway 状态。如果 NodeConfig 在 gateway 和 L3 成功后 失败,则恢复旧 gateway、L3 和 multiplex 状态。

multiplex listener 会先绑定替换端口,再关闭旧 listener。因此端口切换失败不会关闭 当前正在工作的 multiplex listener。

回滚对操作系统资源是 best-effort。如果恢复本身失败,Agent 记录错误,并仍将原始 apply 标记为 rejected。

端口冲突语义

端口检查区分两种情况:

  • 地址已经由当前 ForwarderManager 持有。这是正常热重载场景。Agent 会尽可能原地 更新现有 listener 的 route/upstream 配置。
  • 地址被 manager 外部占用。这是 apply 错误。Agent 拒绝快照,不跳过 listener。

该规则适用于 TCP listener、UDP listener、QUIC listener 和 L3 QUIC ingress listener。

也就是说,请求 :443 的配置不会在 Agent 没有运行 :443 时静默成功。运维人员应在 rejected ConfigApplyReport 中看到包含 already bound outside this manager 的错误。

应用回报

每个非旧快照 apply 尝试后,Agent 都会发送 ConfigApplyReport

  • node_id
  • config_version
  • accepted
  • message
  • runtime_listeners

后端将其写入节点 metadata:

  • agent_config_apply
  • node_port_runtime_listeners
  • node_port_runtime_reported_at

回报更新不会推进 config_updated_at,因此不会创建新配置代际。

运维检查

确认节点是否应用了期望快照:

  1. 查看节点 metadata 中的 agent_config_apply.version
  2. 确认 agent_config_apply.acceptedtrue
  3. 查看 agent_config_apply.message;成功应用时为 applied
  4. 检查 /api/nodes/ports/runtime 或 metadata 中的 node_port_runtime_listeners
  5. 如果 listener 缺失,查看 rejected report 和端口冲突错误。

不改变已存配置、只强制刷新:

bash
curl -X POST "$BACKEND/api/nodes/$NODE_ID/config" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'

不要用这个接口发送内联 L4 rule。应先持久化期望的 L4/L3 资源。

兼容性

protobuf 仍包含 split update variant:

  • ConfigUpdate.pingora
  • ConfigUpdate.l3_tunnels
  • ConfigUpdate.node_config

Agent 仍接受它们,以避免旧控制面工具和测试立即失效。新的生产代码应发送 runtime_snapshot

NexusNet documentation