English Manual
打开 English Manual.
rust-supervisor 手册
语言: English
项目定位
rust-supervisor 是 Rust(编程语言) 任务监督核心库. 它面向 Tokio(异步运行时) 服务, 用声明式模型管理 child(子任务) 的启动, 停止, 重启, 隔离, 状态查询, 事件记录, 健康检查和 Shutdown Without Orphaned Tasks(关闭后不留下孤儿任务).
配置边界使用 rust-config-tree(集中配置树) 0.3.0 与 YAML(数据序列化格式) 文件. 运行时可调参数必须通过这条集中配置路径进入系统.
本项目没有旧接口负担. 使用者应该通过拥有模块路径读取公开类型, 例如 rust_supervisor::runtime::supervisor::Supervisor.
阅读路径
- 快速开始: 从 YAML(数据序列化格式)配置启动最小 supervisor(监督器).
- 配置模型: 理解
SupervisorConfig,ConfigState和配置拒绝启动边界. - 拆分配置与透明数组 Section: 理解
groups.yaml/children.yaml的 split 写法与 CLI(命令行)生成模板. - 监督树: 理解
SupervisorSpec,SupervisorTree和注册表关系. - 任务模型: 理解
ChildSpec,TaskFactory,TaskContext和 readiness(就绪). - 策略模型: 理解重启, 退避, 熔断, 隔离和任务退出分类.
- 运行时控制: 理解
SupervisorHandle的控制命令和幂等语义. - Dashboard(看板): 理解 target process(目标进程), relay(中继) 和 dashboard client(看板客户端) 的三端使用流程.
- 关闭协议: 理解四阶段关闭和 blocking worker(阻塞工作任务)边界.
- 可观测性: 理解事件, 日志, 追踪, 指标, 审计和运行摘要.
- 示例程序: 逐个运行
examples/下的学习示例. - 质量门禁: 运行格式化, 编译, 测试, 文档, SBOM(软件物料清单)和发布检查.
能力边界
supervisor core(监督器核心) 只治理生命周期行为. 高频业务消息属于 data plane(数据面). control plane(控制面) 处理生命周期命令, 当前状态查询, 事件和治理决策.
快速开始
语言: English
步骤指引: 本指南共 5 步(Step 1 of 5 至 Step 5 of 5).
Step 1 of 5: 前置条件
主配置文件是 examples/config/supervisor.yaml. 配置必须通过 rust-config-tree(集中配置树) 0.3.0 加载 YAML(数据序列化格式), 然后形成 ConfigState(配置状态).
Step 2 of 5: 最小运行命令
cargo run --example supervisor_quickstart
该示例执行固定路径:
load_config_from_yaml_file读取 YAML(数据序列化格式)ConfigState::to_supervisor_spec派生SupervisorSpec(监督器规格)Supervisor::start启动 runtime(运行时)current_state查询当前状态,shutdown_tree关闭整棵树.
Step 3 of 5: 最小代码路径
use rust_supervisor::config::loader::load_config_from_yaml_file;
use rust_supervisor::runtime::supervisor::Supervisor;
#[tokio::main]
async fn main() -> Result<(), rust_supervisor::error::types::SupervisorError> {
let state = load_config_from_yaml_file("examples/config/supervisor.yaml")?;
let spec = state.to_supervisor_spec()?;
let handle = Supervisor::start(spec).await?;
let current = handle.current_state().await?;
println!("{current:#?}");
handle.shutdown_tree("operator", "quickstart complete").await?;
Ok(())
}
Step 4 of 5: 运行结果
当前示例用于验证接入路径, 不是业务任务模板. 使用者需要把自己的 worker(工作任务) 放入 ChildSpec(子任务规格) 和 TaskFactory(任务工厂), 不应该在业务代码中分散启动无人管理的后台任务.
Step 5 of 5: 健康自检
启动后, supervisor(监督器) 会向 stdout(标准输出) 打印健康自检 JSON. JSON schema(模式) 定义在 health-selfcheck-schema.md.
预期输出示例:
{
"status": "ready",
"supervisor_version": "0.1.2",
"uptime_secs": 3600,
"children": { "total": 5, "running": 5, "failed": 0 },
"dashboard_link": "connected"
}
如果 status 不是 "ready", 请参考 operations runbook(运维手册) 的故障排查步骤.
入口方法
Supervisor(监督器) 结构体 (src/runtime/supervisor.rs) 提供 3 个入口方法:
| 方法 | 输入 | 适用场景 |
|---|---|---|
Supervisor::start(spec) | SupervisorSpec (已构建的规格) | 编程式启动 |
Supervisor::start_from_config_state(state) | ConfigState (已验证的配置) | 从配置加载器获得配置后启动 |
Supervisor::start_from_config_file(path) | YAML 文件路径 | 从 YAML 文件直接启动 |
3 个方法最终都汇聚到私有方法 start_with_policy() (src/runtime/supervisor.rs),它执行:
- 调用
spec.validate()验证所有子进程声明 - 创建 mpsc 命令通道和 broadcast 事件通道
- 创建
RuntimeControlPlane(运行时控制平面) 和ObservabilityPipeline(可观测管道) - 构建
RuntimeControlState(运行时控制状态) tokio::spawn(run_control_loop(...))启动异步控制循环- 启动
RuntimeWatchdog(运行时看门狗) 监控控制循环健康 - 返回
SupervisorHandle(监督器句柄), 用于后续发送命令(重启, 关闭等)和订阅事件
用法示例
从 YAML 文件启动 — start_from_config_state
完整示例见 examples/supervisor_quickstart.rs,配置文件见 examples/config/supervisor.yaml:
use rust_supervisor::config::loader::load_config_from_yaml_file;
use rust_supervisor::runtime::supervisor::Supervisor;
#[tokio::main]
async fn main() -> Result<(), rust_supervisor::error::types::SupervisorError> {
let state = load_config_from_yaml_file("examples/config/supervisor.yaml")?;
let handle = Supervisor::start_from_config_state(state).await?;
handle.shutdown_tree("operator", "quickstart complete").await?;
Ok(())
}
load_config_from_yaml_file 返回 ConfigState,其 to_supervisor_spec() 方法内部由 start_from_config_state 自动调用.
从 YAML 文件路径直接启动 — start_from_config_file
一步式快捷方式, 内部调用 load_config_from_yaml_file:
use rust_supervisor::runtime::supervisor::Supervisor;
#[tokio::main]
async fn main() -> Result<(), rust_supervisor::error::types::SupervisorError> {
let handle = Supervisor::start_from_config_file("examples/config/supervisor.yaml").await?;
handle.shutdown_tree("operator", "done").await?;
Ok(())
}
编程式构建规格启动 — start
完整示例见 examples/supervisor_tree_story.rs:
use std::sync::Arc;
use rust_supervisor::id::types::ChildId;
use rust_supervisor::runtime::supervisor::Supervisor;
use rust_supervisor::spec::child::{ChildSpec, TaskKind};
use rust_supervisor::spec::supervisor::SupervisorSpec;
use rust_supervisor::task::factory::{TaskResult, service_fn};
#[tokio::main]
async fn main() -> Result<(), rust_supervisor::error::types::SupervisorError> {
let factory = service_fn(|ctx| async move {
ctx.heartbeat();
ctx.mark_ready();
println!("child running at path={}", ctx.path);
TaskResult::Succeeded
});
let child = ChildSpec::worker(
ChildId::new("demo-worker"),
"Demo Worker",
TaskKind::AsyncWorker,
Arc::new(factory),
);
let spec = SupervisorSpec::root(vec![child]);
let handle = Supervisor::start(spec).await?;
let state = handle.current_state().await?;
println!("{state:#?}");
handle.shutdown_tree("operator", "demo complete").await?;
Ok(())
}
ChildSpec::worker() 自动设 task_role = Some(TaskRole::Worker),等价于 YAML 中的 task_role: worker.
TaskRole(任务角色) 行为差异
5 个 TaskRole 变体通过 RoleDefaultPolicy::for_role() 映射到 3 个差异维度,产生完全不同的默认生命周期行为:
| 维度 | Service | Worker | Job | Sidecar | Supervisor |
|---|---|---|---|---|---|
| 成功退出时动作 | Restart(重新启动) | Stop(停止) | Stop(停止) | Restart(重新启动) | Restart(重新启动) |
| 超时时动作 | RestartWithBackoff(重试) | RestartWithBackoff(重试) | StopAndEscalate(停止并上报) | RestartWithBackoff(重试) | RestartWithBackoff(重试) |
| 默认最大重启次数 | 10 | 3 | 1 | 5 | 3 |
| 默认严重等级 | Critical | Standard | Optional | Standard | Critical |
具体差异来自 src/policy/task_role_defaults.rs 中的 5 个构造器:
| Role | Description |
|---|---|
| Service | 常驻服务,成功=重启,最多重试 10 次,严重等级 Critical – 期望永远在线 |
| Worker | 后台任务,成功=停止,最多重试 3 次,标准严重等级– 完成即终止 |
| Job | 一次性任务,成功=停止,超时=直接上报(不再重试),最多重试 1 次,严重等级 Optional – 跑完就结束 |
| Sidecar | 辅助进程,同 Service 的常驻行为但重启预算更低(5 次),且必须绑定 SidecarConfig 指定主服务(见 SidecarConfig 结构体) |
| Supervisor | 嵌套监管树,同 Service 的常驻行为,重启预算 3 次,严重等级 Critical |
EffectivePolicy::merge() 在 task_role 为 None 时会回退到 TaskRole::Worker 并记录警告. semantic_conflicts_for_child() 则检测角色语义冲突(如 Job 搭配永久重启策略会报错).
配置模型和结构模式
语言: English
配置入口
配置入口是 rust_supervisor::config::loader::load_config_from_yaml_file. 它只接受 YAML(数据序列化格式)主配置文件, 示例路径是 examples/config/supervisor.yaml.
配置结构体 SupervisorConfig(监督器配置)包含以下顶层组:
| 组 | 类型 | 描述 |
|---|---|---|
include | Vec<PathBuf> | rust-config-tree(集中配置树) 包含的附加配置文件 |
supervisor | SupervisorRootConfig | 根监督器策略 |
policy | PolicyConfig | 重启, 退避, 心跳, failure window(失败窗口), restart budget(重启预算), meltdown fuse(故障熔断)和 supervision pipeline(监督流水线)容量 |
shutdown | ShutdownConfig | 优雅关闭超时和强制终止等待预算 |
observability | ObservabilityConfig | 事件日志容量和指标/审计开关 |
audit | AuditConfig | 审计存储后端, JSON Lines(逐行 JSON)文件路径和写入失败策略 |
backpressure | BackpressureConfig | 可观测性 subscriber(订阅者) 队列的背压策略, 阈值, 窗口和审计通道容量 |
groups | GroupsConfigSection | group(分组)名称与 group-level restart budget(分组级重启预算); 成员由 children[].group 声明; 支持 split 文件 groups.yaml |
group_strategies | Vec<GroupStrategyConfig> | group(分组)级监督策略, 重启限制和升级策略 |
group_dependencies | Vec<GroupDependencyConfig> | group(分组)之间的故障传播关系 |
child_strategy_overrides | Vec<ChildStrategyOverrideConfig> | child(子任务)级监督策略, 重启限制和升级策略 |
severity_defaults | Vec<SeverityDefaultConfig> | TaskRole(任务角色)到 SeverityClass(严重级别)的默认映射 |
dashboard | Option<DashboardIpcConfig> | 可选的 dashboard IPC(看板进程间通信) socket(仅 Unix) |
children | ChildrenConfigSection | 声明式子任务规格; YAML 中为数组; 支持 split 文件 children.yaml |
配置状态
rust_supervisor::config::configurable::SupervisorConfig 是公开 root configuration struct(根配置结构体). 它支持 confique::Config(配置派生), schemars::JsonSchema(结构模式生成特征), Serialize(序列化) 和 Deserialize(反序列化). 使用者可以用同一个模型完成 YAML(数据序列化格式) 加载, template generation(模板生成) 和 schema generation(结构模式生成).
ConfigState(配置状态) 是校验后的不可变状态. 运行时不应该在其它模块里保存运行时可调常量.
ConfigState::to_supervisor_spec 会派生 SupervisorSpec(监督器规格). 当前实现用配置值填充 supervision strategy(监督策略), 策略默认值, 关闭预算, 健康检查时间, 可观测性容量, backpressure(背压)策略, dynamic supervisor(动态监督器)策略, restart budget(重启预算), failure window(失败窗口), meltdown fuse(故障熔断), supervision pipeline(监督流水线)容量, group(分组)策略和 child(子任务)策略覆盖.
模板与拆分配置
官方单文件 template(模板) 是 examples/config/supervisor.template.yaml.
groups 和 children 使用透明数组 Section(配置段). 它们可以写在根文件里, 也可以通过 include 拆到 groups.yaml 和 children.yaml. split 文件只写数组体, 不写 items:.
- 详细说明: 拆分配置与透明数组 Section
- 生成模板目录:
config/supervisor_config/ - 可运行 split 示例:
cargo run --example split_config_supervisor
生成模板与 schema(结构定义). CLI(命令行) 子命令在顶层, 没有 config 前缀. --config 是 run 与 validate-config 子命令的参数; generate-template 与 generate-schema 以默认路径 examples/config/supervisor.yaml 作为模板来源:
cargo run -- run --config examples/config/supervisor.yaml
cargo run -- validate-config --config examples/config/split/supervisor.yaml
cargo run -- generate-template \
--output config/supervisor_config/supervisor_config.example.yaml
cargo run -- generate-schema \
--output config/supervisor_config/supervisor.schema.json
错误边界
配置加载失败会返回 SupervisorError::FatalConfig. 这些情况会拒绝启动:
顶层检查:
- 配置文件不是 YAML(数据序列化格式).
- 文件无法读取.
- YAML(数据序列化格式)无法解析成
SupervisorConfig. - supervision strategy(监督策略) 不是
OneForOne,OneForAll或RestForOne. - 数值为零.
- 初始退避大于最大退避.
- jitter(抖动)比例不在零到一之间.
policy.restart_budget.window_secs,policy.restart_budget.max_burst或policy.restart_budget.recovery_rate_per_sec不合法.policy.failure_window.window_secs,policy.failure_window.max_count或policy.failure_window.threshold不合法.policy.meltdown.*中的窗口或阈值为零.policy.supervision_pipeline.*中的容量或并发重启限制为零.supervisor.dynamic_supervisor.child_limit为零.- backpressure(背压) 的
warn_threshold_pct不是 1 到 100 之间的数值. - backpressure(背压) 的
critical_threshold_pct不是 1 到 100 之间的数值. - backpressure(背压) 的
warn_threshold_pct大于或等于critical_threshold_pct. - backpressure(背压) 的
window_secs或audit_channel_capacity为零.
子任务声明检查:
- Child ID(子任务标识)和 name(名称)不能为空.
- tags(标签)不能为空.
kind: Supervisor的子任务不能有 factory(工厂);kind: AsyncWorker或kind: BlockingWorker必须有 factory(工厂).- Sidecar(辅助进程)任务角色需要
sidecar_config, 反之亦然. - 依赖循环会被拒绝.
- 分组成员只在
children[].group声明;child.group引用的 group(分组)名称必须在groups中存在. group_strategies和group_dependencies引用的 group(分组)名称必须存在.child_strategy_overrides引用的 child(子任务)名称必须存在.severity_defaults不能为同一个 TaskRole(任务角色)声明多次默认值.
IPC(进程间通信)检查(当 dashboard.enabled = true 时):
target_id不能为空.path是必填字段且必须是绝对路径.- 注册
relay_registration_path是必填字段且必须是绝对路径. lease_seconds必须大于零.heartbeat_interval_seconds必须为正且小于lease_seconds.
Supervisor::start_from_config_file 会在创建 runtime channel(运行时通道) 或派生 control loop(控制循环) 之前拒绝非法配置.
示例配置
supervisor:
strategy: OneForAll
escalation_policy: escalate_to_parent
control_channel_capacity: 256
event_channel_capacity: 256
dynamic_supervisor:
enabled: true
child_limit: 16
policy:
child_restart_limit: 10
child_restart_window_ms: 60000
supervisor_failure_limit: 30
supervisor_failure_window_ms: 60000
initial_backoff_ms: 100
max_backoff_ms: 5000
jitter_ratio: 0.10
heartbeat_interval_ms: 1000
stale_after_ms: 3000
restart_budget:
window_secs: 60
max_burst: 10
recovery_rate_per_sec: 0.50
failure_window:
mode: time_sliding
window_secs: 60
max_count: 5
threshold: 5
meltdown:
child_max_restarts: 3
child_window_secs: 10
group_max_failures: 5
group_window_secs: 30
supervisor_max_failures: 10
supervisor_window_secs: 60
reset_after_secs: 120
supervision_pipeline:
journal_capacity: 100
subscriber_capacity: 10
concurrent_restart_limit: 5
shutdown:
graceful_timeout_ms: 5000
abort_wait_ms: 1000
observability:
event_journal_capacity: 256
metrics_enabled: true
audit_enabled: true
audit:
enabled: true
backend: memory
failure_strategy: fail_closed
max_defer_queue: 1000
backpressure:
strategy: alert_and_block
warn_threshold_pct: 80
critical_threshold_pct: 95
window_secs: 30
audit_channel_capacity: 1024
groups:
- name: core
children:
- api
budget:
window_secs: 60
max_burst: 10
recovery_rate_per_sec: 0.50
- name: upstream
children: []
group_strategies:
- group: core
strategy: OneForOne
restart_limit:
max_restarts: 5
window_ms: 60000
escalation_policy: quarantine_scope
group_dependencies:
- from_group: core
to_group: upstream
propagation: Full
child_strategy_overrides:
- child_id: api
strategy: RestForOne
restart_limit:
max_restarts: 3
window_ms: 30000
escalation_policy: shutdown_tree
severity_defaults:
- task_role: service
severity: Critical
children:
- name: api
kind: supervisor
criticality: critical
tags:
- core
task_role: supervisor
severity: Critical
group: core
restart_policy: transient
dashboard:
enabled: true
target_id: payments-worker-a
path: /tmp/rust-supervisor-demo/payments-worker-a.sock
permissions: "0600"
bind_mode: replace_stale
registration:
enabled: true
relay_registration_path: /tmp/rust-supervisor-demo/dashboard-relay-registration.sock
display_name: "payments worker a"
lease_seconds: 30
registration_heartbeat_interval_seconds: 15
密钥占位符
配置值中引用敏感信息的字段使用 ${SECRET_NAME} 占位符格式.
在启动 supervisor(监督器) 前, 需要将这些占位符替换为实际的环境变量值或密钥管理方案的值. 示例:
dashboard:
security_config:
peer_identity:
allowed_uids: [ "${SUPERVISOR_UID}" ]
dashboard.security_config 不携带审计设置. IPC(进程间通信)审计持久化使用顶层 audit 配置, 因此全局只有一个权威 AuditConfig(审计配置).
supervisor(监督器) 本身不解析运行时占位符; 替换必须在配置加载前完成(例如通过 envsubst 或部署流水线).
TLS(传输层安全协议)由 relay(中继)层(rust-supervisor-relay)通过 wss:// 处理. supervisor(监督器)目标进程只暴露本地 Unix domain socket(Unix 域套接字), 不终结 TLS(传输层安全协议).
升级
本版本不支持原地升级. 如需升级, 请部署新版本的全新实例, 并通过外部 IPC(进程间通信) 接口迁移状态.
拆分配置与透明数组 Section
语言: English
概述
SupervisorConfig(监督器配置) 的 groups(分组) 和 children(子任务) 使用 透明数组 Section(transparent array section). 它们在 YAML(数据序列化格式) 里表现为数组, 在 Rust 里通过 items 字段存储, 加载与模板生成由 rust-config-tree 0.3.0 处理.
一句话: 单文件里写 children: [...], split 文件里只写 [...], 不需要 items: 包裹.
适用字段
| 字段 | Rust 类型 | split 文件 | section schema(配置段结构定义) 顶层类型 |
|---|---|---|---|
groups | GroupsConfigSection | groups.yaml | array |
children | ChildrenConfigSection | children.yaml | array |
访问子项:
#![allow(unused)]
fn main() {
config.children.len();
config.children.as_slice();
config.groups.as_slice();
}
单文件写法
根配置里直接写数组:
groups:
- name: core
children:
- name: api
kind: async_worker
Split 写法
根配置引用 split 文件:
include:
- groups.yaml
- children.yaml
supervisor:
strategy: OneForAll
policy:
child_restart_limit: 10
# ... 其余 policy / shutdown / observability 字段
groups.yaml 与 children.yaml 只写数组体, 不要写 section 根键或 items::
# groups.yaml
- name: core
budget:
window_secs: 60
max_burst: 10
recovery_rate_per_sec: 0.5
# children.yaml
- name: worker
kind: async_worker
criticality: optional
restart_policy: permanent
仓库参考:
- 生成模板目录:
config/supervisor_config/ - 示例配置:
examples/config/split/ - 可运行示例:
cargo run --example split_config_supervisor
支持的 YAML 形状
加载器兼容三种写法:
| 形状 | 示例 |
|---|---|
| 透明数组 | children: [{ name: api }] |
| body-only split | children.yaml 内只有 - name: api |
| 旧式 items 包裹 | children:\n items: [...] |
不要使用 flow 风格 [{ name: worker }]. 模板生成会输出 block YAML(块状 YAML).
CLI(命令行)
使用二进制 rust-tokio-supervisor 或 cargo run. 子命令在顶层, 没有 config 前缀. --config 是 run 与 validate-config 子命令的参数, 不是全局选项.
# 校验并打印摘要(默认 run 子命令)
cargo run -- run --config examples/config/supervisor.yaml
# 校验完整配置树(含 include 合并与运行时校验)
cargo run -- validate-config --config examples/config/split/supervisor.yaml
# 生成 split 模板(含 groups.yaml / children.yaml)
# 模板来源默认为 examples/config/supervisor.yaml
cargo run -- generate-template \
--output config/supervisor_config/supervisor_config.example.yaml
# 生成 schema(结构定义)
cargo run -- generate-schema \
--output config/supervisor_config/supervisor.schema.json
生成后 不需要 再手动去掉 children: 或把 flow 数组改成 block 形式, 库内已完成 normalize(规范化).
代码加载
#![allow(unused)]
fn main() {
use rust_config_tree::config::load_config;
use rust_supervisor::config::{
configurable::SupervisorConfig,
loader::load_config_from_yaml_file,
};
// 直接得到 SupervisorConfig
let config = load_config::<SupervisorConfig>("supervisor.yaml")?;
// 或经过校验得到 ConfigState(配置状态)
let state = load_config_from_yaml_file("supervisor.yaml")?;
}
运行时 default(默认值) 与模板样例
| 场景 | children 结果 |
|---|---|
配置文件完全省略 children | 运行时 [], 不会注入模板样例 worker |
执行 generate-template | children.yaml 可含 worker 样例(来自 #[config(default = ...)]) |
| split 文件 body-only | 正常加载数组内容 |
groups 模板 default 为 [].
IDE(集成开发环境) 补全
split 文件绑定 section schema:
# yaml-language-server: $schema=./children.schema.json
- name: worker
kind: async_worker
children.schema.json 顶层是 array, 不是 { items: [...] } 对象.
在新项目中复用
若在自己的 crate(包) 里新增透明数组 Section:
- 为每个透明数组 Section 声明独立 Struct(结构体), 字段为
items: Vec<T>, 并实现透明数组的Serialize,Deserialize,JsonSchema与访问方法(可参考ChildrenConfigSection,GroupsConfigSection). - 在根配置字段上加
#[schemars(extend("x-tree-split" = true, "x-tree-transparent-array" = true))]. - 实现
ConfigSchema::include_paths. - 用
load_config,write_config_templates,write_config_schemas加载与生成.
详见 rust-config-tree 文档 中 x-tree-transparent-array 说明.
factory_key 配置说明
语言: English
1. 结论
factory_key 是 YAML(配置文件格式) 里的任务工厂 key(键). 它的值是配置文件和 Rust(系统编程语言) 代码约定的名字, 例如 api_server. 它把配置文件中的 worker(工作任务) 声明连接到 Rust(系统编程语言) 代码里注册的 TaskFactory(任务工厂).
配置文件只保存声明, 不保存可执行 closure(闭包). 真正的任务启动逻辑必须由 Rust(系统编程语言) 代码提供.
2. 解决的问题
Supervisor(监督器) 任务树可以通过配置文件声明子任务. 但是 async_worker(异步工作任务) 和 blocking_worker(阻塞工作任务) 在真正启动时需要可执行的 TaskFactory(任务工厂). TaskFactory(任务工厂) 本质上包含 Rust(系统编程语言) 代码和 closure(闭包), 不能安全地直接写入 YAML(配置文件格式).
factory_key 解决这个边界问题. 配置文件写一个约定好的 key(键), Rust(系统编程语言) 代码用同一个 key(键) 注册任务工厂. 启动前, 系统把二者绑定起来.
3. 配置写法
children.yaml 可以这样声明 worker(工作任务):
- name: api
kind: async_worker
factory_key: api_server
- name: exporter
kind: blocking_worker
factory_key: report_exporter
这里的 api_server 和 report_exporter 不是函数名. 它们是配置层任务工厂 key(键). Rust(系统编程语言) 代码必须注册同名 TaskFactory(任务工厂).
4. Rust 代码侧注册流程
Rust(系统编程语言) 代码通过 TaskFactoryRegistry(任务工厂注册表) 建立 key(键) 到 TaskFactory(任务工厂) 的映射.
#![allow(unused)]
fn main() {
use rust_supervisor::spec::child::TaskKind;
use rust_supervisor::task::factory::{TaskResult, service_fn};
use rust_supervisor::task::factory_registry::{
TaskFactoryDescriptor, TaskFactoryRegistry,
};
use std::sync::Arc;
let mut registry = TaskFactoryRegistry::new();
registry.register(TaskFactoryDescriptor::new(
"api_server",
"API Server",
"Runs the API service.",
[TaskKind::AsyncWorker],
Arc::new(service_fn(|_ctx| async { TaskResult::Succeeded })),
))?;
registry.register(TaskFactoryDescriptor::new(
"report_exporter",
"Report Exporter",
"Runs blocking export work.",
[TaskKind::BlockingWorker],
Arc::new(service_fn(|_ctx| async { TaskResult::Succeeded })),
))?;
}
TaskFactoryDescriptor(任务工厂描述符) 同时保存 3 类信息:
key: 配置文件使用的任务工厂 key(键).title和description: Schema(结构定义) 自动补全时展示给编辑器的说明.allowed_kinds: 允许使用这个工厂的任务类型, 例如TaskKind::AsyncWorker或TaskKind::BlockingWorker.
5. 启动时绑定流程
配置加载后, factory_key 仍然只是字符串. 启动前需要把字符串解析为真正的 TaskFactory(任务工厂).
当前绑定路径是:
ConfigState(配置状态) 读取 YAML(配置文件格式) 子任务声明.to_supervisor_spec_with_factories使用TaskFactoryRegistry(任务工厂注册表) 绑定 worker(工作任务).bind_task_factories检查每个 worker(工作任务) 的factory_key.- 注册表找到对应
TaskFactory(任务工厂) 后, 把它写入ChildSpec(子任务规格). Supervisor(监督器) 启动时只看到已经绑定好的可执行任务工厂.
绑定规则是:
- worker(工作任务) 必须声明
factory_key. - Supervisor(监督器) 子节点不能声明
factory_key. - 未注册的
factory_key会导致配置错误. - 工厂不支持当前
TaskKind(任务类型) 时会导致配置错误.
6. 自动补全生成流程
自动补全依赖 JSON Schema(JSON 结构定义). 当前实现不是重写 rust-config-tree(配置树库) 的 Schema(结构定义) 生成逻辑, 而是在基础 Schema(结构定义) 生成之后做后处理.
流程是:
generate-template或generate-schema调用 rust-config-tree(配置树库) 生成基础 Schema(结构定义).supervisor_schema_targets_with_factory_registry取得 root schema(根结构定义) 和 split-section schema(拆分配置段结构定义).- 每个 Schema(结构定义) 先解析成
serde_json::Value(JSON 值). inject_factory_key_completions_if_present查找factory_key字段.- 系统把
TaskFactoryRegistry(任务工厂注册表) 中的 key(键) 写入oneOf(枚举补全候选). - Schema(结构定义) 被重新序列化并写入目标文件.
生成后, children.schema.json 的 factory_key 字段会包含类似内容:
{
"factory_key": {
"description": "TaskFactory registry key used to bind worker children before startup.",
"oneOf": [
{
"const": "api_server",
"description": "Runs the API service.",
"title": "API Server"
},
{
"const": "report_exporter",
"description": "Runs blocking export work.",
"title": "Report Exporter"
}
],
"type": [
"string",
"null"
]
}
}
编辑器读取 children.yaml 顶部的 yaml-language-server(YAML 语言服务器) Schema(结构定义) 指令后, 可以对 factory_key 给出候选值.
7. 命令行为
执行模板生成:
target/debug/rust-tokio-supervisor generate-template
这个命令会生成配置模板, 同时生成带补全信息的 Schema(结构定义).
执行 Schema(结构定义) 生成:
target/debug/rust-tokio-supervisor generate-schema
这个命令只生成 Schema(结构定义), 也会包含 factory_key 的 oneOf(枚举补全候选).
8. 当前边界
factory_key只是配置层声明, 不是可执行代码.- 自动补全候选来自当前命令使用的
TaskFactoryRegistry(任务工厂注册表). - 如果 Rust(系统编程语言) 代码没有注册某个 key(键), 配置文件写这个 key(键) 也无法启动.
- Schema(结构定义) 自动补全只能帮助编辑器提示合法候选, 不能替代启动前绑定校验.
- 运行时添加子任务也会经过同一类绑定校验, 避免配置绕过注册表.
监督树
语言: English
声明模型
SupervisorSpec(监督器规格) 描述一个 supervisor(监督器)节点. 它包含:
path— 监督器稳定路径strategy— 重启范围策略 (OneForOne,OneForAll,RestForOne)children— 声明顺序的子任务规格集合config_version— 生成此规格的配置版本default_restart_policy,default_backoff_policy,default_health_policy,default_shutdown_policy— 子任务不覆盖时继承的策略supervisor_failure_limit— 监督器失败上限, 超限后升级到父级restart_limit— 可选的监督器级重启限制escalation_policy— 可选的监督器级升级策略group_strategies— 分组级策略覆盖group_configs— 分组级重启预算, 成员资格和隔离配置group_dependencies— 故障传播的跨分组依赖边severity_defaults— 每个任务角色的默认严重等级, 用于升级分叉child_strategy_overrides— 逐子任务策略和治理覆盖dynamic_supervisor_policy— 运行时 add_child 接受策略control_channel_capacity— mpsc 命令通道容量event_channel_capacity— broadcast 事件通道容量
ChildSpec(子任务规格) 描述一个 child(子任务). 它包含:
id,name,kind— 稳定标识和任务类型factory— 可选的Arc<dyn TaskFactory>(任务工厂), 用于工作子任务restart_policy,shutdown_policy,health_policy,readiness_policy,backoff_policy— 逐子任务策略覆盖dependencies— 必须在当前子任务之前就绪的子任务标识tags— 低基数分组标签criticality—Critical(关键) 或Optional(可选)task_role— 可选的TaskRole(任务角色), 用于选择默认生命周期策略语义sidecar_config— 可选的边车绑定(角色为Sidecar时必须)severity— 可选的显式严重等级覆盖group— 可选的分组名称, 用于分组级隔离和预算跟踪health_check,readiness— 可选的健康检查/就绪检查配置resource_limits— 可选的资源限制command_permissions— 授予此子任务的命令权限environment,secrets— 环境变量和密钥引用
树构建
SupervisorTree::build 会校验 SupervisorSpec(监督器规格), 再把 children(子任务集合)转换成带路径的节点. 每个 child(子任务)路径来自父路径和 ChildId(子任务标识).
SupervisorPath::root 表示根路径. SupervisorPath::join 用于拼接子路径. SupervisorPath::parent 用于查找父级路径.
启动和关闭顺序
startup_order 按声明顺序返回节点. shutdown_order 按声明顺序的逆序返回节点. 这个顺序是 Shutdown Without Orphaned Tasks(关闭后不留下孤儿任务) 的基础.
重启计划
restart_execution_plan(重启执行计划函数) 根据 tree(监督树) 和 SupervisorSpec(监督器规格) 解析运行时重启范围. 它把 per-child override(子任务级覆盖), group strategy(分组策略), restart limit(重启次数限制), escalation policy(升级策略) 和 dynamic supervisor policy(动态监督器策略) 放入同一个 plan(计划), 运行时控制循环不需要重复编写策略选择逻辑.
注册表
RegistryStore(注册表存储)按 child id(子任务标识), supervisor path(监督器路径) 和声明顺序保存 ChildRuntime(子任务运行态). 运行时控制和当前状态查询不应该绕过注册表直接访问内部状态.
任务模型
语言: English
任务类型
TaskKind(任务类型) 区分 AsyncWorker(异步工作任务), BlockingWorker(阻塞工作任务) 和 Supervisor(监督器节点). blocking worker(阻塞工作任务)不能被当作普通 async worker(异步工作任务)立即强制终止.
任务工厂
TaskFactory(任务工厂)是核心任务构造契约. 每次 attempt(尝试)必须创建 fresh future(新异步任务). service_fn(函数适配器)只是人体工学入口, 它仍然适配到 TaskFactory(任务工厂), 不替换内核模型.
TaskResult(任务结果)区分 Succeeded(成功), Cancelled(已取消) 和 Failed(已失败). Failed 携带 TaskFailure(任务失败)和 TaskFailureKind(任务失败类别).
任务上下文
TaskContext(任务上下文)包含 child id(子任务标识), supervisor path(监督器路径), generation(代次), attempt(尝试次数), cancellation token(取消令牌), heartbeat(心跳)发送入口和 readiness(就绪)发送入口.
worker(工作任务)应该通过 TaskContext::heartbeat 报告健康信号, 通过 TaskContext::mark_ready 报告显式就绪, 通过 TaskContext::is_cancelled 或 TaskContext::cancellation_token 响应关闭.
就绪语义
ReadinessPolicy(就绪策略)支持 Immediate(立即就绪)和 Explicit(显式就绪). 显式就绪的 child(子任务)在报告 ready(已就绪)之前, 不应该被 current state(当前状态)或事件显示为 ready(已就绪).
ChildSpec 与 ChildDeclaration
ChildSpec 和 ChildDeclaration 是什么关系?
语言: English
ChildDeclaration(子任务声明) 是配置和 RPC 进来的对外声明; ChildSpec(子任务规格) 是监督运行时真正拿来注册、启动、重启的内部规格. 二者字段大量重叠, 但职责不同, 中间用 TryFrom 做转换并补默认值.
各自是什么
ChildDeclaration | ChildSpec | |
|---|---|---|
| 模块 | src/spec/child_declaration.rs | src/spec/child.rs |
| 角色 | YAML、add_child 载荷等输入模型 | 注册表、控制循环里的运行时模型 |
| 典型来源 | 配置文件反序列化、动态加子任务请求 | 由声明转换而来, 或代码里直接构造 |
| 能否单独跑起来 | 不能, 没有工厂、没有完整策略对象 | 能, 监督器按它管生命周期 |
ChildDeclaration 侧重可序列化、可校验的声明: 名字、依赖名、环境变量、密钥占位符、health_check / readiness 配置块等, 并带 validate_child_declaration 等规则 (名字格式、${SECRET} 语法等).
ChildSpec 在声明字段之外, 还带上运行时必需的东西, 例如:
- 已生成的
ChildId(子任务标识) (由name推导) factory: Option<Arc<dyn TaskFactory>>(真正干活的任务工厂, 不参与 serde)- 已物化的
HealthPolicy、ReadinessPolicy、ShutdownPolicy、BackoffPolicy isolation、cleanup_paths等运行期字段
怎么连起来
数据流可以看成:
YAML / add_child RPC
|
v
ChildDeclaration ---- validate_child_declaration ----+
| |
| TryFrom<ChildDeclaration> for ChildSpec |
v |
ChildSpec --------------------------------------------+
|
v
注册拓扑、启动子任务、策略流水线、重启/熔断等
转换实现在 child_declaration.rs 的 TryFrom<ChildDeclaration> for ChildSpec, 会做例如:
name->ChildId::new(&decl.name)dependencies里的名字 ->Vec<ChildId>health_check->HealthPolicy(带默认间隔)readiness有配置 ->ReadinessPolicy::Explicit, 否则Immediateshutdown_policy/backoff_policy等在转换时填默认 (声明里未必逐项写出)
动态加子任务时, PendingChild 会同时保留 declaration 和转换后的 child_spec, 审计里还会对声明做 SHA-256 (declaration_hash), 便于对账和补偿.
和共享类型的关系
RestartPolicy、TaskKind、HealthCheckConfig 等公共枚举/配置结构定义在 child.rs, ChildDeclaration 复用它们, 避免两套平行类型. 但顶层容器仍是两个: 声明容器 vs 规格容器.
ChildSpec 构造路径总览
仓库中构造 ChildSpec(子任务规格) 的路径可以分成 6 类. 这些路径面向不同使用场景, 不应该混成一个入口.
| 路径 | 典型入口 | 适用场景 | 校验方式 |
|---|---|---|---|
| Builder(构建器) | ChildSpecBuilder::worker, service, job, sidecar, supervisor, new | Rust(编程语言) 代码里直接拼运行时规格 | build() 调用 ChildSpec::validate() |
| Worker 便捷函数 | ChildSpec::worker(...) | 只需要 worker(后台任务) 默认值包 | 内部委托 ChildSpecBuilder::worker(...).build() |
| 声明转换 | TryFrom<ChildDeclaration> for ChildSpec | YAML(配置文件), RPC(远程过程调用), dynamic add child(动态添加子任务) | 转换前走 validate_child_declaration, 转换后由 supervisor(监督器) 规格校验兜底 |
| Role Template(角色模板) | ServiceTemplate::child_spec, JobTemplate::child_spec 等 | 已经手写 ServiceRole(服务角色特征) 等 trait(特征), 但不想手写 adapter(适配器) 和规格 | 内部调用对应 ChildSpecBuilder |
| Macro(宏) 生成 | #[service], #[worker], #[job], #[sidecar], #[supervisor_role] 生成的 child_spec() | 默认角色接入路径, 使用者只写生命周期方法 | 宏生成代码调用对应 ChildSpecBuilder |
| Serde(序列化和反序列化) | serde_json::from_value::<ChildSpec>(...) | 主要用于测试反序列化默认值和非法枚举 | 不经过 builder(构建器), 使用前必须显式校验或进入后续规格校验 |
几条关键边界:
ChildSpecBuilder::build()是 Rust(编程语言) 代码构造路径的主要出口.- 配置和 RPC(远程过程调用) 不应该直接接收
ChildSpec, 应该先接收ChildDeclaration(子任务声明), 再转换为ChildSpec(子任务规格). Role Template(角色模板) 和Macro(宏) 都不是新的运行时模型. 它们只是把角色生命周期对象装配成 adapter(适配器), 再调用ChildSpecBuilder生成规格.Serde(序列化和反序列化) 可以构造ChildSpec, 因为ChildSpec派生了Deserialize(反序列化特征). 这条路径不会自动调用ChildSpecBuilder::build().
相邻但不算构造 ChildSpec 的路径:
| 入口 | 为什么不算 |
|---|---|
SupervisorSpec::root(Vec<ChildSpec>) | 它接收已经构造好的子任务规格列表, 只构造 supervisor(监督器) 规格 |
SupervisorSpecBuilder::root(Vec<ChildSpec>) | 它包装 supervisor(监督器) 规格构造, 不创建单个子任务规格 |
ConfigState::to_supervisor_spec() | 它把 ConfigState 中已经保存的 Vec<ChildSpec> 组装成 supervisor(监督器) 规格 |
bind_child_factory(...) | 它给已有 ChildSpec 绑定 factory(任务工厂), 不创建新的 ChildSpec |
clone() | 它复制已有 ChildSpec, 不从输入模型生成新规格 |
怎么记
- 写配置、接 API、做声明校验 -> 想
ChildDeclaration - 看监督器怎么管某个子任务、策略引擎读什么 -> 想
ChildSpec - 问「YAML 里写的和运行时用的是不是一回事」-> 同源信息, 不同生命周期阶段: 声明是输入, 规格是落地后的形态
代码内构造
配置和 RPC 仍应使用 ChildDeclaration. 在 Rust 代码里直接构造运行时规格时, 推荐使用 ChildSpecBuilder:
#![allow(unused)]
fn main() {
use rust_supervisor::id::types::ChildId;
use rust_supervisor::policy::task_role_defaults::TaskRole;
use rust_supervisor::spec::child::TaskKind;
use rust_supervisor::spec::child_builder::ChildSpecBuilder;
use rust_supervisor::task::factory::{TaskResult, service_fn};
use std::sync::Arc;
let factory = service_fn(|_ctx| async { TaskResult::Succeeded });
let spec = ChildSpecBuilder::worker(
ChildId::new("worker"),
"worker",
TaskKind::AsyncWorker,
Arc::new(factory),
)
.task_role(TaskRole::Worker)
.tag("invoice")
.build()?;
}
入口方法:
| 方法 | 用途 |
|---|---|
ChildSpecBuilder::worker(...) | 异步或阻塞 worker, 默认值与 ChildSpec::worker 一致 |
ChildSpecBuilder::service(...) | 常驻 service(服务), 自动设置 TaskRole::Service |
ChildSpecBuilder::job(...) | 有限生命周期 job(一次性任务), 自动设置 TaskRole::Job |
ChildSpecBuilder::sidecar(...) | sidecar(边车), 自动设置绑定和主子任务依赖 |
ChildSpecBuilder::supervisor(...) | 嵌套 supervisor, 无 factory |
ChildSpecBuilder::new(...) | 最小骨架, 需自行补 kind 和 factory |
构建出口:
| 方法 | 行为 |
|---|---|
build() | 构造后调用 ChildSpec::validate(), 失败时返回 SupervisorError |
ChildSpec::worker(...) 仍可使用, 内部委托 ChildSpecBuilder::worker(...).build(), 同样返回 Result<ChildSpec, SupervisorError>.
若你关心的是某条具体字段 (例如 task_role 只在哪一侧出现), 可以说字段名, 我可以对照 TryFrom 逐项说明映射与默认值.
ChildSpecBuilder (子任务规格构建器)
语言: English
一句话结论
ChildSpecBuilder 是在 Rust 代码里构造 ChildSpec(子任务规格) 的链式 API(应用程序接口). 配置和 RPC(远程过程调用) 仍应使用 ChildDeclaration(子任务声明). 构建出口是 build() -> Result<ChildSpec, SupervisorError>, 内部会调用 ChildSpec::validate() 做校验.
与 child-spec.md 的关系: 那篇文档说明声明与规格的分工; 本篇专门讲 Builder 的入口, setter(设置器) 与常见用法.
模块路径
#![allow(unused)]
fn main() {
use rust_supervisor::spec::child_builder::ChildSpecBuilder;
}
模块定义在 src/spec/child_builder.rs. 按项目模块边界规则, 没有 pub use 重导出.
什么时候用 Builder
| 场景 | 推荐方式 |
|---|---|
YAML 配置, add_child RPC 载荷 | ChildDeclaration + TryFrom |
| 测试, examples(示例), 代码里直接拼运行时规格 | ChildSpecBuilder |
| 只需 worker 默认 bundle(默认值包), 不想写链式调用 | ChildSpec::worker(...)? (内部委托 Builder) |
旧写法 (逐字段赋值) 仍可能出现在历史代码里, 新代码优先用 Builder.
入口方法
| 方法 | 用途 | 默认值要点 |
|---|---|---|
worker(id, name, kind, factory) | 异步或阻塞 worker | 与 ChildSpec::worker 一致: Transient 重启, Critical 关键性, TaskRole::Worker 等 |
service(id, name, kind, factory) | 常驻 service(服务) | 基于 worker 默认值: TaskRole::Service, Critical 关键性 |
job(id, name, kind, factory) | 有限生命周期 job(一次性任务) | 基于 worker 默认值: TaskRole::Job, Optional 关键性 |
sidecar(id, name, kind, factory, sidecar_config) | 跟随 primary child(主子任务) 的 sidecar(边车) | 基于 worker 默认值: TaskRole::Sidecar, 写入 sidecar_config, 并自动加入 primary child 依赖 |
supervisor(id, name) | 嵌套 supervisor(监督器) | kind = Supervisor, factory = None, task_role = Supervisor, criticality = Critical |
new(id, name) | 最小骨架 | 仅填 id / name 与 baseline(基线) 策略; 调用方需自行补 kind, 以及 worker 所需的 factory |
构建出口
| 方法 | 行为 |
|---|---|
build() | 取出内部 ChildSpec, 调用 validate(), 成功返回 Ok(spec), 失败返回 SupervisorError |
所有入口方法和 setter(设置器) 都返回 ChildSpecBuilder, 只表示“还在构造中“. 只有 build() 会消费 builder(构建器), 并返回最终 ChildSpec.
没有 build_validated(). 校验统一在 build() 内完成.
ChildSpec::worker(...) 同样返回 Result<ChildSpec, SupervisorError>, 实现为 ChildSpecBuilder::worker(...).build().
基本用法
#![allow(unused)]
fn main() {
use rust_supervisor::error::types::SupervisorError;
use rust_supervisor::id::types::ChildId;
use rust_supervisor::policy::task_role_defaults::TaskRole;
use rust_supervisor::spec::child::TaskKind;
use rust_supervisor::spec::child_builder::ChildSpecBuilder;
use rust_supervisor::task::factory::{TaskResult, service_fn};
use std::sync::Arc;
fn build_worker() -> Result<ChildSpec, SupervisorError> {
let factory = Arc::new(service_fn(|_ctx| async { TaskResult::Succeeded }));
ChildSpecBuilder::worker(
ChildId::new("invoice-worker"),
"Invoice Worker",
TaskKind::AsyncWorker,
factory,
)
.task_role(TaskRole::Worker)
.tag("invoice")
.build()
}
}
错误用 ? 向上传播, 或在测试里 build().expect("...").
链式 setter 范围
每个 setter 消费 self 并返回 Self, 可任意顺序链接 (在语义允许的前提下).
策略类: isolation, restart_policy, shutdown_policy, health_policy, readiness_policy, backoff_policy
拓扑与分类: dependencies, dependency, tags, tag, criticality, task_role, without_task_role, sidecar_config, without_sidecar_config, severity, without_severity, group, without_group
配置块: health_check, without_health_check, readiness, without_readiness, resource_limits, without_resource_limits, command_permissions, environment, env_var, secrets, secret, cleanup_paths, cleanup_path
运行时: kind, factory, without_factory (供 new() 或 supervisor 路径使用)
命名约定: 复数字段用 dependencies(...), tags(...); 单条便捷方法用 dependency(...), tag(...). environment / env_var, secrets / secret, cleanup_paths / cleanup_path 同理.
常见组合示例
Service (服务)
常驻服务优先使用 service(...), 不需要手动设置 TaskRole::Service:
#![allow(unused)]
fn main() {
ChildSpecBuilder::service(id, "API Service", TaskKind::AsyncWorker, factory)
.tag("service")
.build()?;
}
Job (一次性任务)
有限生命周期任务优先使用 job(...). 如果需要一次运行后停止, 可以继续覆盖 restart_policy:
#![allow(unused)]
fn main() {
ChildSpecBuilder::job(id, "Nightly Export", TaskKind::AsyncWorker, factory)
.restart_policy(RestartPolicy::Temporary)
.build()?;
}
Sidecar (边车)
跟随主任务的边车优先使用 sidecar(...). 这个入口会写入 sidecar_config, 并把 primary child 自动加入依赖:
#![allow(unused)]
fn main() {
use rust_supervisor::policy::task_role_defaults::SidecarConfig;
ChildSpecBuilder::sidecar(
id,
"Metrics Sidecar",
TaskKind::AsyncWorker,
factory,
SidecarConfig::new(primary_id.clone(), false),
)
.build()?;
}
如果仍然用 setter(设置器) 手动设置 task_role = Sidecar, 就必须同时设置 sidecar_config, 否则 build() 校验失败.
从 new() 拼出 worker
#![allow(unused)]
fn main() {
ChildSpecBuilder::new(ChildId::new("custom"), "custom")
.kind(TaskKind::AsyncWorker)
.factory(factory)
.build()?;
}
数据流 (简图)
ChildSpecBuilder::worker / service / job / sidecar / supervisor / new
|
v
链式 setter (policy, role, deps, env, ...)
|
v
build() --> ChildSpec::validate()
|
+-- Ok(ChildSpec) --> Supervisor::start / 注册拓扑
+-- Err(SupervisorError)
示例程序
可运行演示:
cargo run --example child_spec_builder
源码: examples/child_spec_builder.rs. 覆盖 worker, service, job, sidecar, supervisor, new() 路径, 以及故意失败的 sidecar 组合.
测试与回归
外部测试: src/spec/tests/child_builder_test.rs
| 测试 | 验证点 |
|---|---|
worker_builder_matches_child_spec_worker_defaults | Builder 与 ChildSpec::worker 字段一致 |
supervisor_builder_produces_valid_supervisor_child | supervisor 入口无 factory 且可校验 |
service_builder_sets_service_role | service 入口设置 TaskRole::Service 与 Critical 关键性 |
job_builder_sets_job_role_and_optional_criticality | job 入口设置 TaskRole::Job 与 Optional 关键性 |
sidecar_builder_sets_sidecar_role_binding_and_dependency | sidecar 入口设置绑定并自动加入 primary child 依赖 |
builder_setters_apply_expected_fields | sidecar, dependency, tag 等 setter |
build_rejects_invalid_sidecar_combination | 缺 sidecar_config 时 build() 失败 |
new_builder_can_build_valid_worker_with_factory | new() 路径补全后可构建 |
运行:
cargo test --test child_builder_test
已知边界
TryFrom<ChildDeclaration>的默认值 bundle 尚未与 Builder 完全共用; 两处默认值仍可能独立演进, 改默认值时需同时对照声明转换路径.- Builder 不负责 serde(序列化/反序列化); 动态加子任务仍走
ChildDeclaration. - 未批量迁移历史 examples/tests 中的
ChildSpec::worker; 两种写法在运行时等价 (只要都处理Result).
进一步阅读
child-spec.md—ChildDeclaration与ChildSpec的关系, 以及简短 Builder 介绍docs/architecture.md— 模块边界与禁止重导出规则
策略模型
语言: English
监督策略
SupervisionStrategy(监督策略)决定失败后的重启范围.
OneForOne(一对一)只选择失败 child(子任务).OneForAll(一对全部)选择已选范围内的全部 child(子任务).RestForOne(从失败处开始)选择已选范围内的失败 child(子任务)和之后声明的 child(子任务).
restart_scope 根据 SupervisorTree(监督树), 策略和失败 child id(子任务标识)计算重启范围.
restart_execution_plan(重启执行计划函数) 会把 supervisor strategy(监督器策略), GroupStrategy(分组策略), ChildStrategyOverride(子任务级覆盖), RestartLimit(重启次数限制), EscalationPolicy(升级策略) 和 DynamicSupervisorPolicy(动态监督器策略) 合并成 StrategyExecutionPlan(策略执行计划). child override(子任务级覆盖) 优先于 group strategy(分组策略), group strategy(分组策略) 优先于 supervisor-wide strategy(监督器全局策略). plan(计划)中存储 dynamic_supervisor_enabled(动态监督启用)布尔值; 完整的 DynamicSupervisorPolicy(动态监督器策略)结构体(含 child_limit(子任务上限))由控制循环在 add_child 时评估, 不嵌入 plan(计划).
runtime control loop(运行时控制循环) 现在会接收 child exit(子任务退出), 并在 policy(策略) 返回重启决策时自动执行选定的 StrategyExecutionPlan(策略执行计划). 运行时生命周期事件包含重启范围信息, 让 operator(操作者) 可以看到选中的 strategy(策略), group(分组) 和 child scope(子任务范围).
分组策略和子任务覆盖
GroupStrategy(分组策略) 使用 child tag(子任务标签) 定义更小的重启范围. 一个 child(子任务) 最多只能属于一个已配置 strategy group(策略分组). ChildStrategyOverride(子任务级覆盖) 在单个 child(子任务) 需要比 group(分组) 或 supervisor(监督器) 更严格的重启行为时生效.
GroupConfig(分组配置)在分组层级配置重启预算, 成员资格和隔离. GroupDependencyEdge(分组依赖边)定义故障传播的跨分组依赖.
重启次数限制和升级策略
RestartLimit(重启次数限制) 记录选中计划的最大重启次数和计数窗口. EscalationPolicy(升级策略) 记录重启治理不能停留在本地时的后续动作, 包含 parent escalation(父级升级), tree shutdown(整棵树关闭) 或 scope quarantine(范围隔离).
动态监督器策略
DynamicSupervisorPolicy(动态监督器策略) 控制运行时 add_child(添加子任务) 是否被接受. 它是一个无状态配置对象, 包含 enabled(启用) 和 child_limit(子任务上限) 字段. 控制循环在外部维护当前子任务计数, 在 add_child 时调用 allows_addition(current_child_count). 当 dynamic supervision(动态监督) 被禁用, 或已经达到配置的 child limit(子任务上限) 时, 添加会被拒绝.
重启策略
RestartPolicy(重启策略)包含 Permanent(永久), Transient(瞬时)和 Temporary(临时). PolicyEngine(策略引擎)读取 TaskExit(任务退出), 失败类别和重启策略, 输出 RestartDecision(重启决策).
退避和抖动
BackoffPolicy(退避策略)描述初始延迟, 最大延迟和 jitter(抖动)比例. 同名的类型有两个:
spec::child::BackoffPolicy— 用于ChildSpec(子任务规格), 字段为initial_delay(初始延迟),max_delay(最大延迟),jitter_ratio(抖动比例, 0.0-1.0 浮点值).policy::backoff::BackoffPolicy— 由运行时策略引擎使用, 字段为initial(初始),max(最大),jitter_mode(抖动模式, 枚举:Disabled(禁用),Deterministic(确定性),FullJitter(完全抖动),DecorrelatedJitter(解相关抖动)),jitter_percent(抖动百分比) 和reset_after(稳定后重置).
测试可以使用 JitterMode::Deterministic(确定性抖动), 避免依赖随机结果.
熔断和隔离
MeltdownPolicy(熔断策略)限制一个窗口内的重启或失败次数, 分为 child(子任务), group(分组) 和 supervisor(监督器) 三个层级. 超过 child-level fuse(子任务级熔断)会进入 quarantine(隔离). 超过 group-level fuse(分组级熔断)会升级到 supervisor(监督器)层. 超过 supervisor-level fuse(监督器级熔断)会升级到父级.
任务退出分类
TaskExit(任务退出)区分成功, 取消, 类型化失败, panic(恐慌)和 timeout(超时). 策略层通过 TaskFailureKind(任务失败类别, 包含 Panic(恐慌), Timeout(超时)和类型化失败类别)读取类型化分类, 不应该从字符串推断行为.
运行时控制
语言: English
控制入口
SupervisorHandle(监督器句柄)是运行时控制入口. 它通过命令通道把请求发送给 runtime control loop(运行时控制循环), 并返回 CommandResult(命令结果).
控制命令
以下是通过命令通道发送的 ControlCommand(控制命令)枚举变体:
add_child— 当DynamicSupervisorPolicy(动态监督器策略) 允许新增 child(子任务) 时, 接受 dynamic child manifest(动态子任务清单文本).remove_child— 把目标 child(子任务) 的运行状态记录标记为Removed(已移除), 向活动 attempt(尝试) 发送 cancel(取消), 并在 attempt(尝试) 退出后移除运行状态记录.restart_child— 请求目标 child(子任务)重启.pause_child— 把目标 child(子任务) 的运行状态记录标记为Paused(已暂停), 向活动 attempt(尝试) 发送 cancel(取消), 并暂停自动重启.resume_child— 恢复目标 child(子任务)治理.quarantine_child— 把目标 child(子任务) 的运行状态记录标记为Quarantined(已隔离), 向活动 attempt(尝试) 发送 cancel(取消), 并阻止自动重启.shutdown_tree— 关闭整棵监督树.current_state— 返回当前SupervisorState(监督器状态), 并在CurrentState.child_runtime_records(当前状态子任务运行状态记录集合)中暴露每个 child(子任务) 的运行状态事实.
句柄方法
以下是不经过 ControlCommand(控制命令)的 SupervisorHandle(监督器句柄)方法:
subscribe_events— 通过broadcast::Receiver(广播接收器)订阅生命周期事件.is_alive— 快速判断 runtime control loop(运行时控制循环) 是否仍可接收普通控制命令.health— 返回RuntimeHealthReport(运行时健康报告), 包含alive(是否存活),state(控制面状态),started_at_unix_nanos(启动时间),last_observed_at_unix_nanos(最近观测时间),failure(失败原因) 和exit_report(退出报告).join— 等待 runtime control plane(运行时控制面)进入最终态, 并重复返回同一个RuntimeExitReport(运行时退出报告).shutdown— 只关闭 runtime control plane(运行时控制面), 不替代shutdown_tree(监督树关闭).
子任务运行状态控制
PauseChild(暂停子任务), RemoveChild(移除子任务) 和 QuarantineChild(隔离子任务) 是本功能定义的停止类控制命令. 这 3 条命令都会返回 CommandResult::ChildControl(子任务控制命令结果), 结果中包含 ChildControlResult(子任务控制结果). 旧的 CommandResult::ChildState(子任务状态命令结果) 不再属于公开结果形状.
PauseChild(暂停子任务) 会把 ChildRuntimeState.operation(子任务运行状态记录操作) 写为 Paused(已暂停). 如果当前存在活动 attempt(尝试), runtime control loop(运行时控制循环) 会向该 attempt(尝试) 发送 cancel(取消), 并把停止进度推进到 CancelDelivered(已送达取消). 暂停期间, supervision strategy(监督策略) 不会针对该 child(子任务) 自动重启.
RemoveChild(移除子任务) 会把 ChildRuntimeState.operation(子任务运行状态记录操作) 写为 Removed(已移除). 如果当前存在活动 attempt(尝试), runtime control loop(运行时控制循环) 会先发送 cancel(取消), 等 attempt(尝试) 退出后再从 child_runtime_states(子任务运行状态记录集合) 中物理删除记录. 如果当前没有活动 attempt(尝试), runtime control loop(运行时控制循环) 会返回 NoActiveAttempt(无活动尝试) 结果, 然后删除运行状态记录.
QuarantineChild(隔离子任务) 会把 ChildRuntimeState.operation(子任务运行状态记录操作) 写为 Quarantined(已隔离). 如果当前存在活动 attempt(尝试), runtime control loop(运行时控制循环) 会发送 cancel(取消). 隔离后的运行状态记录仍然保留, 但是 supervision strategy(监督策略) 不会继续自动重启该 child(子任务). 操作者仍然可以后续执行 RemoveChild(移除子任务).
这 3 条停止类控制命令不会同步等待 child future(子任务 future) 结束. 如果 child(子任务) 长时间忽略 cancel(取消), 后续 CurrentState(当前状态) 或重复停止类控制命令会触发 reconcile_stop_deadlines(调和停止截止时间), 并通过 ChildControlFailure(子任务控制失败原因) 暴露停止失败.
CurrentState(当前状态) 会返回 child_runtime_records(子任务运行状态记录集合). 每条 ChildRuntimeRecord(子任务运行状态记录) 都按声明顺序排列. 构造过程只做非阻塞读取, 不等待 child future(子任务 future), 不执行额外 I/O(输入输出). 该集合是查看运行状态事实的主入口.
RestartChild(重启子任务) 和 ResumeChild(恢复子任务) 仍然是既有命令. 本功能只要求它们不破坏运行状态事实, 不把它们定义为新增生命周期语义.
完整契约见 child-runtime-state-control.md.
ChildControlResult(子任务控制结果) 字段
child_id(子任务标识): 被控制的 child(子任务) 稳定标识.attempt(尝试): 命令实际作用的活动 attempt(尝试). 没有活动 attempt(尝试) 时为None(无值).generation(代次): 命令实际作用的 generation(代次). 没有活动 attempt(尝试) 时为None(无值).operation_before(命令前操作): 命令到达时的ChildControlOperation(子任务控制操作).operation_after(命令后操作): 命令处理后的ChildControlOperation(子任务控制操作).status(状态): 当前 attempt(尝试) 的ChildAttemptStatus(子任务尝试状态). 没有活动 attempt(尝试) 时为None(无值).cancel_delivered(取消已送达): 本次命令是否真正发送了 cancel(取消).stop_state(停止状态): 本次命令处理后的ChildStopState(子任务停止状态).restart_limit(重启次数限制): 当前RestartLimitState(重启次数限制状态), 包含窗口, 上限, 已使用次数, 剩余次数和耗尽标志.liveness(存活状态): 当前ChildLivenessState(子任务存活状态), 包含最后心跳时间, 心跳是否陈旧和 readiness(就绪状态).idempotent(幂等): 本次命令是否复用了已经存在的目标状态.failure(失败原因): 当前控制失败原因. 没有失败时为None(无值).generation_fence(代次围栏): 可选的GenerationFenceOutcome(代次围栏结果), 由重启控制命令使用.admission_conflict(准入冲突): 可选的AdmissionConflict(准入冲突)细节, 当并发请求被拒绝时提供.
ChildRuntimeRecord(子任务运行状态记录) 字段
child_id(子任务标识): 运行状态记录对应的 child(子任务) 稳定标识.path(路径): child(子任务) 在 supervisor tree(监督树) 中的路径.generation(代次): 当前活动 generation(代次). 没有活动 attempt(尝试) 时为None(无值).attempt(尝试): 当前活动 attempt(尝试). 没有活动 attempt(尝试) 时为None(无值).status(状态): 当前 attempt(尝试) 的ChildAttemptStatus(子任务尝试状态).operation(操作): 当前ChildControlOperation(子任务控制操作), 可能是Active(活跃),Paused(已暂停),Quarantined(已隔离)或Removed(已移除).liveness(存活状态): 当前ChildLivenessState(子任务存活状态).restart_limit(重启次数限制): 当前RestartLimitState(重启次数限制状态).stop_state(停止状态): 当前ChildStopState(子任务停止状态).failure(失败原因): 最近一次ChildControlFailure(子任务控制失败原因). 当stop_state(停止状态)为Failed(停止失败)时必须为Some(有值).generation_fence_phase(代次围栏阶段): 当前GenerationFencePhase(代次围栏阶段), 用于仪表盘投影.pending_restart(待重启): 可选的PendingRestartSummary(待重启摘要), 用于代次围栏后排队的重启.
幂等语义
重复控制命令不应该制造不可恢复错误. 已暂停的 child(子任务)再次暂停时返回当前状态. 已隔离的 child(子任务)再次隔离时返回当前状态. 已完成 shutdown(关闭)后再次关闭时返回已有关闭结果.
join(等待结束) 会缓存控制循环的最终 RuntimeExitReport(运行时退出报告). 同一个 handle(句柄) 重复调用 join(等待结束) 时, 每次都返回相同结果, 不会再次消费底层退出接收器.
shutdown(关闭) 只请求 runtime control loop(运行时控制循环) 正常退出. 如果控制面已经 completed(已完成) 或 failed(失败), 再次调用 shutdown(关闭) 会直接返回已有最终报告. shutdown_tree(监督树关闭) 仍然负责 child task(子任务)和整棵监督树的关闭语义.
运行时健康
is_alive(是否存活) 是低成本状态判断. 当控制面处于 alive(存活) 时, 它返回 true. 当控制面处于 starting(启动中), shutting_down(正在关闭), completed(已完成) 或 failed(失败) 时, 它返回 false.
health(健康报告) 返回结构化状态. 控制面异常退出后, health(健康报告) 仍然可以读取: alive(是否存活), state(状态), started_at_unix_nanos(启动时间), last_observed_at_unix_nanos(最近观测时间), failure(失败原因, 包含 phase(阶段), reason(原因), panic(恐慌)标记, recoverable(可恢复)标记) 和 exit_report(退出报告). 普通控制命令在控制面结束后会返回包含同一退出原因的 SupervisorError(监督器错误).
动态添加
运行时会在接受 manifest(清单文本) 前执行 dynamic addition(动态添加) 治理. 当 dynamic supervision(动态监督) 被禁用, 或 declared child count(声明子任务数量) 加 dynamic child count(动态子任务数量) 已经达到配置上限时, add_child(添加子任务) 会被拒绝. current_state(当前状态) 的 child_count(子任务数量) 包含已经接受的 dynamic manifest(动态清单文本).
审计数据
每个控制命令都带有 CommandMeta(命令元数据), 包含 command_id(命令标识), requested_by(请求者) 和 reason(原因). 这些字段必须是非空文本. SupervisorHandle(监督器句柄) 会在命令进入 channel(通道) 前拒绝空值, runtime control loop(运行时控制循环) 也会在执行命令前再次校验. 这样做可以保证人工操作, dashboard IPC(看板进程间通信) 转发和内部控制调用都留下可追踪的审计来源.
事件负载 CommandAudit(命令审计)额外记录 target_path(目标路径) 和 accepted_at_unix_nanos(接受时间) 用于审计事件和问题追踪.
Dashboard(看板) 三端使用流程
语言: English
dashboard(看板) 功能由三个仓库共同完成. rust-supervisor 只负责 target process(目标进程) 本机 IPC(进程间通信) 和 shared contract(共享契约). ~/rust-supervisor-relay 负责 relay(中继) 和外部 wss:// session(会话). ~/rust-supervisor-ui 负责 browser dashboard client(浏览器看板客户端).
下面的 screenshot(截图) 展示 dashboard client(看板客户端) 的 target list(目标列表), topology(拓扑), state(状态) 和 runtime stream(运行时流) 集中视图.

三端职责
rust-supervisor: target process(目标进程) 读取SupervisorConfig(监督器配置), 在dashboard.enabled=true时打开 Unix domain socket(Unix 域套接字), 并生成 state(状态), event record(事件记录), log record(日志记录), command result(命令结果) 和 registration heartbeat(注册心跳).rust-supervisor-relay: relay(中继) 监听 registration socket(注册套接字), 保存 target registry(目标注册表), 对外提供wss://dashboard session(看板会话), 校验 mTLS(双向传输层安全协议认证) 和 allowed IPC path prefix(允许的进程间通信路径前缀), 并把会话命令转发到 target process(目标进程).rust-supervisor-ui: dashboard client(看板客户端) 通过wss://连接 relay(中继), 显示 target list(目标列表), topology(拓扑), state(状态), event stream(事件流), log tail(日志尾部) 和 command audit(命令审计).
Generation fencing(代次隔离) 投影
IPC(进程间通信) 可见的子任务控制封装携带可空的 generation_fence(代次隔离结果) 载荷。运行时一旦登记 pending restart(待重启) 请求, DashboardChildRuntimeRecord(仪表盘子任务运行状态记录) 也会镜像 pending_restart 摘要字段。
本地演示流程
- 先启动 relay(中继). 它必须先监听 registration socket(注册套接字), target process(目标进程) 才能注册自己.
cd ~/rust-supervisor-relay
cargo run -- --config examples/config/dashboard-relay.local.yaml
- 再启动 target process(目标进程). 它会打开本机 IPC(进程间通信) socket(套接字), 并向 relay(中继) 发送 registration heartbeat(注册心跳).
cd ~/rust-supervisor
cargo run --example demo -- --config examples/config/supervisor.local.yaml
- 最后启动 dashboard client(看板客户端). browser script(浏览器脚本) 只连接 relay(中继), 不直接读取 target process(目标进程) 的本机 IPC(进程间通信) socket(套接字).
cd ~/rust-supervisor-ui
VITE_SUPERVISOR_RELAY_URL=wss://localhost:9443/supervisor npm run dev
运行顺序
relay(中继) 接收到 registration heartbeat(注册心跳) 后, 只把 target process(目标进程) 放入 target registry(目标注册表). 这个注册动作不会触发 event(事件) 或 log(日志) 主动推送. dashboard client(看板客户端) 建立 authenticated dashboard session(已认证看板会话) 并选择目标后, relay(中继) 才连接 target process(目标进程) IPC(进程间通信) socket(套接字), 读取 state(状态), 并按会话请求订阅 events.subscribe 或 logs.tail.
控制命令必须从 dashboard client(看板客户端) 发起, 经过 relay(中继) session validation(会话校验), 再发送到 target process(目标进程). 每个命令必须带上 operator identity(操作者身份), target identity(目标身份) 和 reason(原因). dangerous command(危险命令) 还必须在 client(客户端) 中完成确认.
验证命令
cd ~/rust-supervisor
cargo test --test dashboard_config_test --test dashboard_protocol_shape_test --test dashboard_state_test --test dashboard_stream_test --test dashboard_performance_test
cargo test --manifest-path ~/rust-supervisor-relay/Cargo.toml
npm --prefix ~/rust-supervisor-ui run test
npm --prefix ~/rust-supervisor-ui run build
npm --prefix ~/rust-supervisor-ui run test:e2e:three-end
生产接入注意事项
target process(目标进程) 只能暴露本机 Unix domain socket(Unix 域套接字), 不能直接把 IPC(进程间通信) 暴露到外网. relay(中继) 对外只能使用 wss://. mTLS(双向传输层安全协议认证) client certificate(客户端证书) 由 browser(浏览器) 或 operating system certificate store(操作系统证书库) 选择, page script(页面脚本) 不能读取证书私钥. dashboard.path, registration.relay_registration_path 和 relay(中继) 的 allowed IPC path prefix(允许的进程间通信路径前缀) 必须同时匹配, 否则目标会注册失败或被 relay(中继) 拒绝连接.
关闭协议
语言: English
正式术语
本项目使用 Shutdown Without Orphaned Tasks(关闭后不留下孤儿任务) 描述关闭目标. root shutdown(根关闭)完成后, runtime(运行时)不应该留下 orphan task(孤儿任务).
四阶段
关闭协议包含四个阶段:
- request stop(请求停止): 接受关闭原因并传播 cancellation token(取消令牌).
- graceful drain(优雅排空): 等待 child(子任务)自行结束.
- abort stragglers(强制终止拖尾任务): 对超时的异步任务执行强制终止或升级.
- reconcile(状态对账): 统一 registry(注册表), current state(当前状态), metrics(指标)和 event journal(事件日志缓冲区).
顺序
启动按声明顺序执行. 关闭按声明顺序的逆序执行. 这个规则由 startup_order 和 shutdown_order 提供.
阻塞任务边界
BlockingWorker(阻塞工作任务)表示 spawn_blocking(阻塞任务启动) 或其它不能假设立即 abort(强制终止)的任务. 关闭超时后, runtime(运行时)应该记录不可立即终止边界, 并按照升级策略处理.
关闭原因
ShutdownCause(关闭原因)记录 requested_by(请求者)和 reason(原因). 它应该进入审计和诊断输出.
完成结果
shutdown_tree(关闭监督树)返回 ShutdownResult(关闭结果). 流水线完成后 ShutdownResult.report 含有 ShutdownPipelineReport(关闭流水线报告), 包含逐子任务结果, 对账报告和 dashboard socket(看板套接字)状态. 核心 runtime(运行时)不拥有 dashboard IPC socket(看板进程间通信套接字)时, 报告会把 socket status(套接字状态)记录为 NotOwned(非运行时拥有).
可观测性
语言: English
事件模型
SupervisorEvent(监督器事件)描述一次 lifecycle fact(生命周期事实). 它包含 When(何时), Where(何处), What(发生内容), sequence(序号)和 correlation id(关联标识).
When(何时)记录 wall time(墙钟时间), monotonic time(单调时间), uptime(运行时长), generation(代次)和 attempt(尝试次数). Where(何处)记录 supervisor path(监督器路径), child id(子任务标识), parent id(父标识)和任务名称. What(发生内容)记录状态迁移, 策略决定, 健康状态, 退出原因或控制命令.
管线输出
observability pipeline(可观测性管线)把同一事实同步为这些信号:
SupervisorEvent(监督器事件).- structured log(结构化日志).
- tracing(结构化追踪) span(追踪范围)和 event(追踪事件).
- metrics(指标).
- audit event(审计事件).
- event journal(事件日志缓冲区).
- test recorder(测试记录器).
指标标签
metrics label(指标标签)必须保持低基数. 可以使用 supervisor path(监督器路径), child id(子任务标识), state(状态), decision(决定)和 failure category(失败类别). 不应该使用错误全文, 用户输入或无界动态值.
真实关闭流水线
ShutdownTree(关闭监督树) 执行真实 shutdown pipeline(关闭流水线) 后, observability pipeline(可观测性管线) 必须能看到每个阶段的事实. ChildShutdownCancelDelivered(子任务取消已送达) 表示 runtime(运行时) 已经向运行中的 child attempt(子任务尝试) 发送 CancellationToken(取消令牌). ChildShutdownGraceful(子任务优雅完成) 表示 child task(子任务) 在 graceful drain(优雅排空) 时间预算内返回. ChildShutdownAborted(子任务已强制中止) 表示 runtime(运行时) 已经对滞留任务请求 abort(强制中止). ChildShutdownLateReport(子任务迟到报告) 表示 child task(子任务) 在正常关闭核算窗口之后才返回. ShutdownCompleted(关闭完成) 表示 pipeline(流水线) 已经输出最终 reconcile report(对账报告).
metrics(指标) 使用低基数标签记录关闭事实. supervisor_shutdown_duration_seconds(监督器关闭耗时秒数) 记录完整 pipeline(流水线) 耗时. supervisor_shutdown_child_outcomes_total(监督器子任务关闭结果总数) 按 status(状态) 和 phase(阶段) 计数, 不把 child_id(子任务标识) 写入指标标签. supervisor_shutdown_abort_total(监督器关闭强制中止总数) 按 bounded reason(有界原因) 计数. supervisor_shutdown_late_reports_total(监督器关闭迟到报告总数) 按 phase(阶段) 计数.
audit event(审计事件) 会记录 cancel delivered(取消已送达), graceful outcome(优雅结果), abort outcome(强制中止结果), late report(迟到报告) 和 completed reconcile(完成对账). 核心 runtime(运行时) 不拥有 dashboard IPC socket(看板进程间通信套接字) 时, reconcile report(对账报告) 会把 socket status(套接字状态) 写成 NotOwned(非运行时拥有).
诊断回放
event journal(事件日志缓冲区)保存固定容量的最近事件. RunSummary(运行摘要)从事件日志, current state(当前状态)和策略决定生成诊断摘要, 用于解释 meltdown(熔断), 关闭超时或父级升级.
示例程序
语言: English
快速开始
cargo run --example supervisor_quickstart
supervisor_quickstart 读取 examples/config/supervisor.yaml, 派生 SupervisorSpec(监督器规格), 启动 supervisor(监督器), 查询 current state(当前状态), 然后关闭整棵树.
配置树
cargo run --example config_tree_supervisor
config_tree_supervisor 展示 rust-config-tree(集中配置树) 0.3.0 的 YAML(数据序列化格式)配置加载路径, 并打印派生后的 SupervisorSpec(监督器规格).
拆分配置
cargo run --example split_config_supervisor
split_config_supervisor 从 examples/config/split/supervisor.yaml 加载配置. 该根文件通过 include 引用 body-only(仅数组体) 的 groups.yaml 和 children.yaml. 详见 拆分配置与透明数组 Section.
重启策略实验
cargo run --example restart_policy_lab
restart_policy_lab 展示 TaskFailure(任务失败), TaskFailureKind(任务失败类别), RestartPolicy(重启策略), canonical spec::supervisor::SupervisionStrategy(规范归属的监督策略) 和 RestartDecision(重启决策) 的基本形状.
关闭树
cargo run --example shutdown_tree
shutdown_tree 展示 request stop(请求停止), graceful drain(优雅排空), abort stragglers(强制终止拖尾任务)和 reconcile(状态对账)四个阶段, 然后执行 shutdown_tree.
可观测性探针
cargo run --example observability_probe
observability_probe 订阅事件流, 查询当前状态, 打印一个事件, 然后执行关闭. 它用于检查 observability(可观测性)接入路径.
监督树故事
cargo run --example supervisor_tree_story
supervisor_tree_story 声明 market feed(行情输入), risk engine(风控引擎) 和 audit sink(审计输出) 三个 child(子任务), 展示 dependencies(依赖), tags(标签), criticality(关键程度), explicit readiness(显式就绪), startup order(启动顺序), shutdown order(关闭顺序) 和 RestForOne(从失败处开始) restart scope(重启范围).
运行时控制故事
cargo run --example runtime_control_story
runtime_control_story 启动真实 supervisor(监督器), 执行 add_child, pause_child, resume_child, quarantine_child, current_state, subscribe_events 和 shutdown_tree. 它覆盖 operator control(操作员控制) 和 audit event(审计事件) 的组合场景.
策略失败矩阵
cargo run --example policy_failure_matrix
policy_failure_matrix 对 Permanent(永久), Transient(瞬时) 和 Temporary(临时) restart policy(重启策略) 分别输入 success(成功), external dependency(外部依赖), fatal bug(致命缺陷) 和 panic(恐慌) 退出结果, 同时展示 deterministic jitter(确定性抖动) 和 meltdown tracker(熔断跟踪器).
诊断回放
cargo run --example diagnostic_replay
diagnostic_replay 构造 deterministic event(确定性事件), 写入 event journal(事件日志缓冲区), 回放 failure(失败), backoff(退避) 和 restart(重启) 事实, 然后生成 metrics(指标) 样本和 RunSummary(运行摘要). 它用于排查生产事件后的 replay(回放) 和 report(报告) 路径.
运维手册
语言: English
说明: 每个步骤都列出了关键节点的期望 metrics(指标) 取值. 如果观测值与期望值不符, 请按升级路径处理或参考链接章节.
P1-001: 监督器进程崩溃
症状: supervisor(监督器) 进程意外退出; child(子任务) 变为孤儿进程.
| 步骤 | 操作 | 期望指标 | 预计耗时 |
|---|---|---|---|
| 1 | 检查进程状态: pgrep -x supervisor | exit code == 0(运行中) 或 exit code == 1(未运行) | 1min |
| 2 | 如未运行, 检查最近日志: journalctl -u supervisor -n 50 | 日志以 ShutdownPhase::Completed(计划内) 或 Panic(意外) 结尾 | 2min |
| 3 | 如意外崩溃: 收集 core dump 和 backtrace(回溯) | Core dump 文件存在于 /tmp/ | 2min |
| 4 | 重启 supervisor: cargo run --release --example supervisor_quickstart | health.status == "ready" 在 30s 内 | 5min |
| 5 | 验证子任务重连: 检查 dashboard IPC(看板进程间通信) | dashboard_link == "connected" | 2min |
升级路径: 如果重启失败两次, 携带 core dump 和日志升级到 L2 工程团队. 总预计耗时: 12min(在 15min SLA(服务等级协议) 内).
P1-002: 子任务崩溃循环
症状: child(子任务) 反复失败并重启; current_state 显示重启计数过高.
| 步骤 | 操作 | 期望指标 | 预计耗时 |
|---|---|---|---|
| 1 | 查询当前状态: handle.current_state() | children.failed > 0 且 child_runtime_records[].restart_count > 阈值 | 1min |
| 2 | 检查 journal(事件日志) 中的子任务退出原因 | TaskExit::Panicked 或 TaskExit::Failed 附带原因字符串 | 2min |
| 3 | 如重启预算耗尽: restart_budget.tokens == 0 | 预算耗尽, 子任务自动 quarantine(隔离) | 1min |
| 4 | 移除或替换故障 child spec: handle.remove_child() | CommandResult::Accepted | 2min |
| 5 | 验证无残留 slot: 检查 current_state() | children.running == target_count | 2min |
升级路径: 如果在 10min 内未确定根因, 携带退出原因和 journal 片段提单. 总预计耗时: 8min.
P1-003: Dashboard IPC(看板进程间通信) 断开
症状: health.dashboard_link == "disconnected"; dashboard(看板) UI(用户界面) 无数据.
| 步骤 | 操作 | 期望指标 | 预计耗时 |
|---|---|---|---|
| 1 | 检查 IPC socket(套接字) 路径: ls -la /tmp/supervisor.sock | socket(套接字) 文件存在且权限正确 | 1min |
| 2 | 检查 relay(中继) 进程: pgrep -x relay | 进程在运行 | 1min |
| 3 | 重启 relay: kill -TERM <relay_pid> 等待自动重启 | Supervisor 自动拉起 relay; dashboard_link == "connected" 在 10s 内 | 3min |
| 4 | 如仍然断开, 重启 supervisor 进程 (dashboard IPC 生命周期与 supervisor 进程绑定) | health.dashboard_link == "connected" | 2min |
升级路径: 如果 IPC socket 路径冲突(错误包含 field_path="dashboard.path"), 检查 deployment guide(部署指南) 的 socket path 配置.
总预计耗时: 7min.
P1-004: 运行时饥饿
症状: 控制循环迭代停滞; health.uptime_secs 仍在增长但事件未处理.
| 步骤 | 操作 | 期望指标 | 预计耗时 |
|---|---|---|---|
| 1 | 检查 Tokio runtime(异步运行时) metrics(指标): handle.health().control_loop_iterations | iterations_per_sec > 0 | 1min |
| 2 | 如停滞, 检查是否有 blocking(阻塞) 任务: 审查 child task(子任务) 列表 | 无 child 处于 BlockForever 或 IgnoreCancel 状态 | 2min |
| 3 | Quarantine(隔离) 可疑子任务: handle.quarantine_child() | Child 标记为 Quarantined | 2min |
| 4 | 验证恢复: health.control_loop_iterations 增长 | 5s 后 iterations_per_sec > 0 | 3min |
升级路径: 如果 quarantine(隔离) 所有非关键子任务后饥饿仍未解除, 携带运行时 metrics(指标) 快照升级到 L2. 总预计耗时: 8min.
常见问题(FAQ)
语言: English
基础概念
ChildDeclaration(子任务声明)和 ChildSpec(子任务规格)到底有什么区别?
ChildDeclaration(子任务声明)是 YAML(数据序列化格式)配置和 add_child(追加子任务)RPC(远程过程调用)载荷中使用的输入模型, 侧重可序列化、可校验的声明. ChildSpec(子任务规格)是运行时真正用来注册、启动、重启的运行时模型, 包含已生成的 ChildId(子任务标识)、Arc<dyn TaskFactory>(任务工厂)和已物化的策略对象.
详细说明见 ChildSpec 与 ChildDeclaration.
Supervisor(监督器)启动后有哪几种入口方法?
Supervisor(监督器)结构体提供 3 个入口方法:
| 方法 | 输入 | 适用场景 |
|---|---|---|
Supervisor::start(spec) | SupervisorSpec(已构建的规格) | 编程式启动 |
Supervisor::start_from_config_state(state) | ConfigState(已验证的配置) | 从配置加载器获得配置后启动 |
Supervisor::start_from_config_file(path) | YAML 文件路径 | 从 YAML 文件直接启动 |
三个方法最终都汇聚到 start_with_policy(), 执行验证、创建通道、启动控制循环并返回 SupervisorHandle(监督器句柄).
Shutdown Without Orphaned Tasks(关闭后不留下孤儿任务)是什么意思?
这是本项目的核心关闭目标. 当根 supervisor(监督器)完成关闭后, runtime(运行时)下不能留下任何孤儿 task(任务). 实现方式是通过四阶段关闭协议(request stop -> graceful drain -> abort stragglers -> reconcile), 以及按声明顺序逆序关闭所有子任务, 确保每个 child(子任务)都被妥善终止.
配置相关
YAML 配置中的 children 字段支持哪些子任务字段?
children 在 YAML(数据序列化格式) 里是数组, 在 Rust 里对应 ChildrenConfigSection(子任务配置段), 通过 .as_slice() 访问子项. 每个声明支持以下字段:
| 类别 | 字段 | 说明 |
|---|---|---|
| 标识 | name | 子任务名称, 必填, 不能为空 |
| 类型 | kind | async_worker, blocking_worker 或 supervisor |
| 关键性 | criticality | critical(关键)或 optional(可选) |
| 重启策略 | restart_policy | permanent(永久), transient(瞬时)或 temporary(临时) |
| 依赖 | dependencies | 依赖的其他子任务名称列表 |
| 健康检查 | health_check | 健康检查间隔、超时等配置 |
| 就绪检查 | readiness | 显式就绪检查配置 |
| 资源限制 | resource_limits | CPU(中央处理器)、内存等资源约束 |
| 命令权限 | command_permissions | 允许此子任务执行的命令 |
| 环境变量 | environment | 键值对环境变量列表 |
| 密钥引用 | secrets | ${SECRET_NAME}(密钥占位符)格式的密钥引用 |
| 标签 | tags | 低基数分组标签 |
| 任务角色 | task_role | service, worker, job, sidecar, supervisor |
完整配置示例见配置模型.
如何把 groups 和 children 拆成独立 YAML 文件?
在根配置里写 include, split 文件只写数组体:
include:
- groups.yaml
- children.yaml
# children.yaml
- name: worker
kind: async_worker
详见 拆分配置与透明数组 Section. 可运行 cargo run --example split_config_supervisor.
配置文件不写 children 会怎样?
运行时是空列表 [], 不会自动注入模板样例 worker. 模板生成命令才会写入样例条目.
配置校验会拒绝哪些情况?
配置加载失败会返回 SupervisorError::FatalConfig. 拒绝启动的情况包括:
- 配置文件不是 YAML 格式或无法读取
- 监督策略不是
OneForOne,OneForAll或RestForOne - 数值为零或超出合法范围
- 初始退避大于最大退避
- jitter(抖动)比例不在 0.0 到 1.0 之间
- 重启预算、失败窗口、熔断配置不合法
- 子任务声明中存在依赖循环
- 子任务 ID 或名称不能为空
- Sidecar(辅助进程)任务角色缺少
sidecar_config - Dashboard IPC(进程间通信)路径不是绝对路径
详细错误列表见配置模型.
运行时控制
add_child(追加子任务)的五步事务是什么?
add_child(追加子任务)把“解析 -> 校验 -> 注册 -> 拉起 -> 审计持久化“五步连成一串, 当作同一桩事务执行:
- 解析: 把 RPC 载荷反序列化为
ChildDeclaration(子任务声明) - 校验: 执行
validate_child_declaration(校验子任务声明), 检查名称格式、依赖名称存在性、密钥占位符语法等 - 注册: 更新拓扑, 把新 child(子任务)插入注册表, 执行环路检测
- 拉起: 通过
TaskFactory(任务工厂)创建并启动 child future(子任务异步任务) - 审计持久化: 写入审计记录, 包含声明 SHA-256(安全散列算法)哈希
任一步失败时, 整体回退到调用前的拓扑视图, 或者写入 compensating(补偿)段落供善后使用.
运行时控制命令有哪些是幂等的?
重复控制命令不会制造不可恢复错误:
- 已暂停的 child(子任务)再次暂停返回当前状态
- 已隔离的 child(子任务)再次隔离返回当前状态
- 已完成 shutdown(关闭)后再次关闭返回已有关闭结果
join(等待结束)会缓存最终RuntimeExitReport(运行时退出报告), 重复调用返回同一结果
pause(暂停), quarantine(隔离)和 remove(移除)有什么区别?
三个都是停止类控制命令, 但行为不同:
| 命令 | operation(运行状态记录操作) | 运行状态记录保留 | 自动重启 |
|---|---|---|---|
pause_child(暂停子任务) | Paused(已暂停) | 保留 | 暂停期间不自动重启 |
quarantine_child(隔离子任务) | Quarantined(已隔离) | 保留 | 不再自动重启 |
remove_child(移除子任务) | Removed(已移除) | attempt(尝试)退出后物理删除 | 不适用 |
暂停可以恢复(resume_child), 隔离后也可以手动移除. 移除是最终操作, 运行状态记录会被物理删除.
策略与失败处理
RestartPolicy(重启策略)的三个取值分别适用于什么场景?
| 取值 | 行为 | 适用场景 |
|---|---|---|
Permanent(永久) | 始终重启 | 关键服务, 如 API 服务、数据库连接 |
Transient(瞬时) | 只在特定失败类别下重启 | 外部依赖失败时重启, 致命缺陷时不重启 |
Temporary(临时) | 最多重启一次 | 一次性任务(job), 失败后不重试 |
Meltdown(熔断)的三个层级是如何级联的?
熔断策略(MeltdownPolicy)限制一个窗口内的重启或失败次数, 分为三个层级:
- child-level(子任务级): 超过
child_max_restarts/child_window_secs-> 进入 quarantine(隔离) - group-level(分组级): 超过
group_max_failures/group_window_secs-> 升级到 supervisor(监督器)层 - supervisor-level(监督器级): 超过
supervisor_max_failures/supervisor_window_secs-> 升级到父级
熔断触发后, 会在 reset_after_secs(熔断重置秒数)后自动重置.
可观测性
如何订阅生命周期事件?
通过 SupervisorHandle::subscribe_events()(监督器句柄订阅事件方法)获取 broadcast::Receiver(广播接收器). 事件类型为 SupervisorEvent(监督器事件), 包含 When(何时, 墙钟/单调时间/运行时长/代次/尝试次数)、Where(何处, 监督器路径/子任务标识/任务名称)和 What(发生内容, 状态迁移/策略决定/健康状态/退出原因/控制命令).
event journal(事件日志缓冲区)满了怎么办?
event journal(事件日志缓冲区)是固定容量的环形缓冲区. 写满时覆盖最旧条目. 容量通过 observability.event_journal_capacity(可观测性事件日志缓冲区容量配置)配置. 但如果使用的是 add_child 事务专用的审计通道, 写满时不会静默覆盖, 而是返回 Err(AuditStorageFailure)(审计存储失败错误).
Dashboard(看板)
Dashboard(看板)功能需要哪三个仓库配合?
dashboard(看板)功能由三个仓库共同完成:
| 仓库 | 职责 |
|---|---|
rust-supervisor (本项目) | target process(目标进程)本机 IPC(进程间通信)和 shared contract(共享契约) |
~/rust-supervisor-relay | relay(中继)和外部 wss:// session(会话) |
~/rust-supervisor-ui | browser dashboard client(浏览器看板客户端) |
target process(目标进程)只暴露本机 Unix domain socket(Unix 域套接字), 不能直接把 IPC(进程间通信)暴露到外网.
IPC(进程间通信)支持哪些方法?
支持的 method(方法)包括: hello, state, events.subscribe, logs.tail, command.restart_child, command.pause_child, command.resume_child, command.quarantine_child, command.remove_child, command.add_child 和 command.shutdown_tree.
项目与构建
target/debug/rust-tokio-supervisor generate-template 不带参数会做什么?
generate-template 不带参数时, 不会输出到 stdout(终端). 它默认写入 config/<root-config-name>/<root-config-name>.example.yaml.
以当前项目为例:
# 运行后没有终端输出
./target/debug/rust-tokio-supervisor generate-template
# 但实际文件已写入
ls config/supervisor_config/
# supervisor_config.example.yaml
# supervisor_config.schema.json
详细参数:
# 指定输出路径
./target/debug/rust-tokio-supervisor generate-template --output /tmp/my-config.yaml
# 同时生成 JSON Schema
./target/debug/rust-tokio-supervisor generate-template --schema /tmp/schema.json
输出格式由文件扩展名推断, 未知或无扩展名时默认使用 YAML(数据序列化格式).
为什么 Cargo.toml 里只声明了一个 [[bin]](rust-tokio-supervisor), 但 target/debug/ 下有多个二进制文件?
Cargo 有两种二进制目标声明方式:
- 显式声明: 在
Cargo.toml中用[[bin]]条目声明, 如src/main.rs->rust-tokio-supervisor - 自动发现:
src/bin/目录下的每个.rs文件自动成为一个 binary(二进制)目标, 文件名就是目标名
所以 Cargo.toml 中只看到 [[bin]] name = "rust-tokio-supervisor", 但 src/bin/generate_supervisor.rs 和 src/bin/generate_supervisor_config.rs 被 Cargo 自动发现, 产生了额外的二进制文件.
注意:
src/bin/目录在功能完成后可能会被清理或移动, 以保持项目结构整洁.
常见错误
SupervisorError::FatalConfig 是什么?
FatalConfig(致命配置错误)表示配置加载阶段检测到不可恢复的错误. 错误信息中会包含 field_path(字段路径, JSON Pointer 格式)和 hint(提示段落), 帮助定位具体问题.
add_child 返回 Err(SupervisorShuttingDown)(监督器关闭中错误)怎么办?
这表明 supervisor(监督器)正在执行关停流程, 无法接受新的 add_child 请求. 应该等待 supervisor 完成关闭后重新启动, 再重试添加操作.
add_child 返回 Err(ChildLimitExceeded)(子任务数量超限错误)怎么办?
运行时 child(子任务)数量已经达到上限(当前上限 1000). 需要先移除不必要的子任务(remove_child), 或者调整 dynamic_supervisor.child_limit(动态监督器子任务上限配置)配置.
审计存储失败时会发生什么?
当审计通道(环形缓冲区)写入失败时:
- add_child 会进入 compensating(补偿)流程并返回
Err(AuditStorageFailure)(审计存储失败错误) - 拓扑视图回退到调用前的状态
- 不会留下孤立的半解析状态
质量门禁
语言: English
基线命令
cargo fmt --check
cargo check
cargo test
cargo doc --no-deps
cargo package --list
scripts/check-coding-standard.sh
scripts/check-maintainability.sh
scripts/generate-sbom.sh
scripts/validate-sbom.sh
cargo publish --dry-run
文档同步
manual(手册), docs(工程文档), README(说明文档), examples(示例程序), quickstart(快速开始), public API(公开接口)契约和 glossary(词汇表)需要同步. 公开接口, 配置模式, 示例行为或 observability signal(可观测性信号)变化时, 文档必须同轮更新.
编码标准
scripts/check-coding-standard.sh 检查发布物料, 示例文件, 主配置, 文档标点和禁止兼容表达. 中文文档必须使用英文标点.
可维护性
scripts/check-maintainability.sh 检查 manual(手册)的 zh 与 en 同构入口, docs(工程文档)中 quality gate(质量门禁)与 parallel governance(并行治理)页面的 zh 与 en 同构入口, 示例数量是否满足契约, validation artifact(验证产物), Shutdown Without Orphaned Tasks(关闭后不留下孤儿任务)术语和 rust-config-tree(集中配置树)术语.
SBOM 和发布
scripts/generate-sbom.sh 生成 artifacts/sbom/rust-supervisor.cdx.json 和 artifacts/sbom/rust-supervisor.spdx.json. scripts/validate-sbom.sh 校验文件存在, JSON(数据交换格式)形状, package(包)名称, Cargo.lock 摘要, 以及 secret(密钥), token(令牌), 本地绝对路径和构建临时目录泄漏.