可观测性、测试与发布
本节目标
让单个实例可追溯、系统退化可度量、失败路径可复现,并通过版本化发布和运行手册安全运营工作流。
三类可观测信号
- 事件/日志回答“这个实例发生了什么”。记录状态迁移、步骤、attempt、错误码和操作主体。
- 指标回答“整体是否退化”。例如队列年龄、成功率、补偿率、尾延迟和成本。
- trace回答“时间花在了哪里、跨哪些服务”。通过上下文传播关联触发器、消息、步骤、API、LLM 和工具调用。
OpenTelemetry 是生成、收集和导出 traces、metrics、logs 的厂商中立框架。Semantic Conventions 当前页面仍为 1.43.0;消息 spans 页面在 2026-07-22 仍标注 Development,所以采用前应固定版本并核对稳定性,不要把草案字段写死成永久合同。对异步消息,规范以 producer/consumer 的 context propagation 与 span link 关联链路;一条 trace 不是一串可靠的父子关系,也不应因批处理而强造单一 parent。
事件字段与隐私
建议记录可关联的日志字段;只有低基数维度才适合作为指标 label:
workflow.name、workflow.version、instance_id、step、attempt_id、operation_id、state_from/to、event_id、trace_id、error.code、duration、receipt_id、outcome.status、result_fingerprint。
不要把订单全文、完整提示、模型响应、密钥或工具参数默认写入日志。高基数字段(如每个用户 ID、实例 ID 或完整 operation_id)不适合直接作为指标 label,否则成本和查询稳定性变差;可放日志并通过受控查询关联。
相关性不等于可信身份
trace_id、traceparent、消息 metadata 和前端传来的 tenant 字段只用于关联或诊断,不能成为授权输入。入口验证后,应由服务端把 actor、授权范围、策略版本和实例/资源关系写入受控审计事件;执行时仍按当前授权重新检查。这样既能保留跨系统因果链,也不会让可伪造的可观测性字段越过权限边界。
必备指标
| 维度 | 指标示例 | 能发现什么 |
|---|---|---|
| 入口 | 触发量、去重/冲突率 | 上游重复或协议问题 |
| 排队 | 队列长度、最老消息年龄 | 消费能力不足 |
| 步骤 | 成功率、attempt、错误分类 | 依赖或版本退化 |
| 恢复 | 租约接管、未知结果对账 | worker/网络故障 |
| 补偿 | 触发率、失败率、人工积压 | 业务一致性风险 |
| 审批 | 等待时长、过期/拒绝率 | 人工瓶颈与策略问题 |
| 端到端 | p50/p95/p99 时长、deadline 违约 | 用户体验与尾延迟 |
| LLM | 任务质量、无效结构、token/成本 | 模型或提示回归 |
告警要配运行手册和责任人。对已自动重试成功的单次错误可作为信息事件;重试耗尽、补偿失败、积压年龄和质量门槛跌破才升级为操作告警。
测试金字塔
- schema、条件、幂等键等纯函数单元测试;
- DAG 重名、未知依赖、环和 handler 注册静态检查;
- producer/consumer 合同测试与旧版本夹具;
- 外部依赖 mock 的步骤集成测试;
- 虚拟时间测试调度、退避、等待和审批过期;
- 故障注入:重复事件、超时、崩溃窗口、迟到结果、补偿失败;
- 入口安全测试:伪造/过期签名、重放、同身份异载荷、已撤销 actor 与越权资源;
- 从真实脱敏历史恢复/重放的兼容测试;
- 端到端与容量测试;LLM 节点另有固定评测集。
顺利路径全部通过不足以证明可靠。最重要的测试往往是“副作用提交后崩溃”“同一键不同参数”“旧批准用于新状态”和“补偿本身永久失败”。
发布策略
工作流定义、handler、schema、提示和模型配置都要版本化。新定义不能无条件覆盖运行中实例:
- 新实例使用新版本;
- 旧实例继续旧 worker,或通过已测试的兼容代码恢复;
- 需要迁移时生成可审计迁移事件;
- 先影子验证,再灰度少量新实例;
- 以错误率、积压、补偿率、任务质量和成本作为回退门槛。
回滚代码不会撤销已经发生的付款、通知或发布。发布计划要包含外部副作用的对账与补偿路径。
安全部署最低线
- 工作流定义只允许受审查来源,CI 验证 schema、链接、测试与依赖;
- worker 使用非管理员服务身份和最小权限;
- 密钥来自安全存储,日志与检查点做敏感字段检查;
- 网络出口、工具和目标资源 allowlist 在服务端执行;
- 定义、容器/包和迁移脚本可追溯到版本;
- 审批/人工操作具备经验证身份、理由和按风险保护的审计证据;普通日志本身不自动构成“不可抵赖”证明。
Windows 11 / PowerShell 7 运行手册模板
# 1. 进入项目并确认 Python # 先确认执行环境,而不是把部署故障误判为工作流逻辑错误
Set-Location -LiteralPath 'D:\path\to\workflow' # 替换为自己的工作流项目绝对路径,-LiteralPath 不把通配符当作模式
python --version # 记录实际解释器版本,便于发布时重现验证环境
# 2. 先验证定义与测试;-B 不生成 pyc # 发布前先证明静态合同和离线行为仍成立
python -B .\examples\workflow_engine.py --validate # 只校验 DAG/契约定义,不触发真实业务任务
python -B -m unittest discover -s .\examples -p 'test_*.py' -v # 发现并详细执行示例目录中的全部回归测试
# 3. 查看当前定义哈希和计划变更;不要在这里输出密钥 # 哈希可关联版本,但不应取代敏感配置管理
Get-FileHash -Algorithm SHA256 .\examples\workflow.json # 记录当前工作流定义内容摘要,便于灰度与回退时精确比对
# 4. 灰度后检查队列年龄、失败分类和补偿/审批积压 # 这些信号能发现吞吐、下游失败或人工瓶颈
# 5. 达到回退门槛时停止新触发,排空或固定旧实例版本 # 回退是受控状态迁移,不是删除历史运行记录Linux 服务可把相同步骤放入 CI 和受控部署脚本;额外核对 systemd/container 的停止宽限期、只读文件系统、非 root 用户、健康检查和信号处理。
故障运行手册
| 告警 | 先确认 | 安全动作 | 禁止动作 |
|---|---|---|---|
| 队列年龄上升 | 到达率、消费率、依赖限流 | 降低入口、扩容、延后低优先级 | 无限增加 retry |
| 重复副作用 | 幂等表、键/参数哈希 | 暂停该步骤并对账 | 直接删状态重跑 |
| 补偿失败 | 主动作结果、补偿 attempt | 转人工队列、保留证据 | 强改为 completed |
| 新版错误率高 | 按定义版本切片 | 停止新实例使用新版 | 让旧实例自动跟随 latest |
| 审批积压 | 过期率、责任人、风险级别 | 升级/重新分派/安全过期 | 批量自动批准 |
练习与自测
为订单流程列出至少 15 个测试,必须覆盖重复触发、同键不同金额、并发领取、审批过期、迟到结果、主动作崩溃窗口、补偿失败、旧检查点和新版定义。再写一页“支付步骤异常”的运行手册。
自测:
- 日志、指标和 trace 分别回答什么?
- 为什么指标 label 不宜放任意实例 ID?
- 定义回滚为何不能撤销外部副作用?
- 发布新 worker 前最有价值的历史兼容测试是什么?
下一步
继续 离线 DAG 工作流项目。
参考资料
- OpenTelemetry Documentation(访问于 2026-07-22)
- OpenTelemetry Semantic Conventions 1.43.0(访问于 2026-07-22)
- OpenTelemetry:Messaging Spans(页面仍标注 Development,访问于 2026-07-22)
- NIST SP 800-218:SSDF 1.1
- Temporal Platform Documentation(产品实现参考,访问于 2026-07-22)