文件
T

107 行
6.7 KiB
Markdown
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
# 企微公网健康告警防抖与诊断增强设计
## 1. Architecture and Boundaries
本任务保持现有“ARM systemd timer → 公网探测器 → 脱敏 summary → 共用企微 helper”的边界,不引入守护进程、数据库或外部状态服务。
- 探测器负责网络阶段分类、连续样本计数、恢复确认和抖动周期判定。
- 告警 helper 继续负责 summary 校验、消息文案、Webhook 安全读取和发送侧去重。
- 安装脚本继续固定 5 分钟 timer、受控路径、root 所有权和默认 disabled。
- 本地合同测试通过 stub、受控 socket/DNS 失败和测试 capture 验证,不连接真实机器人。
## 2. Failure Classification Contract
分类优先级固定为:
1. `socket.gaierror` 或等价解析失败 → `dns_resolution_failed`;
2. TCP 建连拒绝、重置、不可达及其他建连类错误 → `tcp_connection_failed`;
3. TLS 证书或握手校验失败 → `tls_validation_failed`;
4. socket/请求超时 → `request_timeout`;
5. 非 2xx HTTP → `http_unavailable`;
6. 响应超过上限 → `response_too_large`;
7. 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
```text
healthy --失败1--> failure_pending --失败2--> alerted(发送公网健康告警)
alerted --成功1--> recovery_pending --成功2--> recovery_watch(发送公网健康恢复)
recovery_pending --失败--> alerted(不发送恢复;持续事件仍去重)
```
失败阈值统计持续不可用样本,告警使用达到阈值时的最新稳定类别。已告警期间相同类别不重复发送;类别发生变化时沿用现有最多一次更新通知语义。
### 3.3 Recovery watch and flapping flow
```text
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
1. 校验 URL、状态目录、状态文件及阈值。
2. 读取并严格校验/迁移探测状态。
3. 执行一次 `/ready` 探测并生成成功或稳定失败类别。
4. 用当前时间和样本驱动纯状态转换,得到新状态及可选通知候选。
5. 原子写入 `0600` 探测状态;有通知候选时才写脱敏 summary 并调用 helper,由 helper 去重或重试。
6. 探测失败仍返回非零,健康样本返回零;通知发送或本地状态失败继续 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 写入继续受独立批准门禁控制。