6.7 KiB
企微公网健康告警防抖与诊断增强设计
1. Architecture and Boundaries
本任务保持现有“ARM systemd timer → 公网探测器 → 脱敏 summary → 共用企微 helper”的边界,不引入守护进程、数据库或外部状态服务。
- 探测器负责网络阶段分类、连续样本计数、恢复确认和抖动周期判定。
- 告警 helper 继续负责 summary 校验、消息文案、Webhook 安全读取和发送侧去重。
- 安装脚本继续固定 5 分钟 timer、受控路径、root 所有权和默认 disabled。
- 本地合同测试通过 stub、受控 socket/DNS 失败和测试 capture 验证,不连接真实机器人。
2. Failure Classification Contract
分类优先级固定为:
socket.gaierror或等价解析失败 →dns_resolution_failed;- TCP 建连拒绝、重置、不可达及其他建连类错误 →
tcp_connection_failed; - TLS 证书或握手校验失败 →
tls_validation_failed; - socket/请求超时 →
request_timeout; - 非 2xx HTTP →
http_unavailable; - 响应超过上限 →
response_too_large; - JSON、编码或
{data: {ok: true}}合同不满足 →response_contract_invalid。
异常类型按从具体到通用的顺序匹配,避免 OSError 提前吞掉 DNS、TLS 或超时。对无法进一步安全细分的连接阶段异常归入 tcp_connection_failed,但不把异常文本写入状态、summary 或日志。
3. State Model
3.1 State schema
探测状态升级为显式版本,字段只包含:
phase:healthy、failure_pending、alerted、recovery_pending、recovery_watch、flapping;activeCategory/lastRecoveredCategory:稳定类别 token 或null;consecutiveFailures/consecutiveSuccesses/flapTransitions:有界非负整数;recoveredAt/healthySince:必要时存在的非负 Unix 秒时间戳。
旧版 schema 只含连续失败数和最后类别。部署时采用保守兼容迁移:有效旧状态映射为等价的 healthy 或 failure_pending,不凭旧状态补发消息;非法或未知 schema 继续 fail closed。告警 helper 自身状态 schema 保持向后兼容。
3.2 Normal incident flow
healthy --失败1--> failure_pending --失败2--> alerted(发送公网健康告警)
alerted --成功1--> recovery_pending --成功2--> recovery_watch(发送公网健康恢复)
recovery_pending --失败--> alerted(不发送恢复;持续事件仍去重)
失败阈值统计持续不可用样本,告警使用达到阈值时的最新稳定类别。已告警期间相同类别不重复发送;类别发生变化时沿用现有最多一次更新通知语义。
3.3 Recovery watch and flapping flow
recovery_watch
--60分钟内同类失败达到2次--> flapping(发送一次链路反复抖动提醒)
--不同类别失败达到2次-----> alerted(作为新事件发送普通告警)
--观察窗到期且健康--------> healthy(静默)
flapping
--任意失败/短时成功-------> flapping(只更新脱敏计数)
--连续健康满30分钟--------> healthy(静默退出)
“连续健康满 30 分钟”以第一条连续成功样本的时间为起点,期间任何失败都会清空 healthySince。状态转换实现为可直接传入当前时间的纯函数,使合同测试可使用合成时间,不依赖等待真实 30/60 分钟,也不增加生产测试参数。
4. Notification Contract
应用健康事件使用专用标题:
FAIL_APP_HEALTH:OA 公网健康告警;PASS_APP_HEALTH_READY:OA 公网健康恢复;FAIL_APP_HEALTH_FLAPPING:OA 公网健康链路反复抖动。
正文只保留状态、稳定类别和操作提示。helper 根据应用健康状态选择专用文案,其他备份/dead-man/提醒状态继续使用原有通用标题。抖动事件采用独立、白名单内的稳定状态 token,防止与普通失败去重键互相覆盖。
状态转换返回“通知候选”而不是自行发送:处于普通故障、已确认恢复或抖动故障时,可在后续同类样本上重复交给 helper 评估,由 helper 的现有状态完成去重;如果上次发送失败且 helper 状态未落盘,下一次样本可以重试。抖动期的健康样本和静默退出不产生恢复候选,由此保证抖动退出不产生额外恢复消息。
5. Data Flow and Failure Handling
- 校验 URL、状态目录、状态文件及阈值。
- 读取并严格校验/迁移探测状态。
- 执行一次
/ready探测并生成成功或稳定失败类别。 - 用当前时间和样本驱动纯状态转换,得到新状态及可选通知候选。
- 原子写入
0600探测状态;有通知候选时才写脱敏 summary 并调用 helper,由 helper 去重或重试。 - 探测失败仍返回非零,健康样本返回零;通知发送或本地状态失败继续 fail closed。
状态先落盘再通知,保持当前“状态不可安全保存则不发送误导消息”的边界。若通知失败,命令返回失败并保留可诊断的稳定错误;后续同类样本再次提交候选,helper 仅在自身发送状态尚未落盘时重试,合同测试必须证明成功发送后不会重复。
6. Compatibility and Trade-offs
- 2 次恢复确认会把恢复消息延后约 5 分钟,这是减少短时成功噪声的明确代价。
- 同类抖动期不逐次通知,会降低每次瞬断的可见性;本地计数和最终稳定退出状态保留诊断能力。
- 不同类别复发仍作为新事件处理,消息可能多于“全部静默”,但避免把新根因错误吞掉。
- 不修改
/ready合同、timer 周期、Webhook 文件、summary 隐私边界或非应用健康告警语义。
7. Rollout and Rollback
- 本地阶段仅提交代码、合同测试和必要文档,不触碰 ARM/DNS/真实企微。
- rollout 获批后先以
install模式部署 disabled unit,核对文件权限、unit 内容、旧状态迁移和只读探测,再单独批准enable。 - 启用后先观察分类计数和消息频率;DNS 调整只以实际
dns_resolution_failed证据为依据。 - 回滚时停用 timer,恢复上一版探测器/helper/unit;保留并备份脱敏状态。若旧版不能读取新 schema,则在明确目标文件后将新状态移出状态目录,由旧版建立健康基线,避免伪造恢复通知。
8. Security and Privacy
- 禁止写入 URL、域名、IP、文件路径、异常文本、响应 payload、Webhook 或 Secret。
- capture 仅在既有显式测试模式下可用,测试输出权限保持
0600。 - 所有状态枚举和类别经过白名单/token 校验;计数与时间戳必须有类型和范围检查。
- 生产、企业微信和 DNS 写入继续受独立批准门禁控制。