273 lines
7.8 KiB
Markdown
273 lines
7.8 KiB
Markdown
# 崩溃保护与 MTBF 统计设计文档
|
||
|
||
## 概述
|
||
|
||
本文档描述服务端代理进程的崩溃保护机制和可靠性统计功能。
|
||
|
||
## 功能目标
|
||
|
||
1. **崩溃保护**:防止代理进程连续崩溃导致的无限重启循环
|
||
2. **崩溃统计**:记录崩溃次数、时间、退出代码等信息
|
||
3. **MTBF 计算**:收集数据用于计算平均故障间隔时间(Mean Time Between Failures)
|
||
|
||
---
|
||
|
||
## 一、崩溃保护机制
|
||
|
||
### 1.1 触发条件
|
||
|
||
| 参数 | 值 | 说明 |
|
||
|------|-----|------|
|
||
| 窗口期 | 5 分钟 | `CRASH_WINDOW_MS = 300000` |
|
||
| 阈值 | 3 次 | `CRASH_THRESHOLD = 3` |
|
||
| 快速崩溃定义 | 30 秒内退出 | `FAST_CRASH_TIME_MS = 30000` |
|
||
|
||
**触发逻辑**:在 5 分钟窗口期内,代理进程发生 3 次快速崩溃(启动后 30 秒内异常退出),则触发崩溃保护。
|
||
|
||
### 1.2 判定标准
|
||
|
||
只有同时满足以下条件才计入崩溃:
|
||
- 退出代码 ≠ 0(正常退出不计入)
|
||
- 运行时间 < 30 秒(长时间运行后退出不计入)
|
||
|
||
**设计原则**:程序正常退出必须返回 0,任何非 0 退出代码都视为异常退出。包括:
|
||
- Windows 异常(如 `0xC0000005` ACCESS_VIOLATION)
|
||
- 自定义错误码(如 `1001` CONFIG_ERROR)
|
||
- 其他非零退出码
|
||
|
||
### 1.3 保护行为
|
||
|
||
触发崩溃保护后:
|
||
1. 服务停止自动重启代理进程
|
||
2. 写入 `AgentCrashProtected = 1` 到配置
|
||
3. 下次程序启动时检测到此标志,切换到普通运行模式
|
||
4. 弹出提示框告知用户
|
||
|
||
### 1.4 状态持久化
|
||
|
||
崩溃窗口状态持久化到注册表,确保服务重启后状态不丢失:
|
||
|
||
配置 section: `[crash]`
|
||
|
||
| 配置键 | 类型 | 说明 |
|
||
|--------|------|------|
|
||
| `winCount` | int | 窗口期内崩溃次数 |
|
||
| `winStart` | string (uint64) | 窗口起始时间 (GetTickCount64) |
|
||
|
||
**服务重启时的恢复逻辑**:
|
||
|
||
```
|
||
加载保存的状态
|
||
↓
|
||
检查 savedFirstCrashTime <= 当前时间?
|
||
├─ 否 → 系统重启过,清除状态
|
||
└─ 是 → 检查是否仍在 5 分钟窗口内?
|
||
├─ 否 → 窗口已过期,清除状态
|
||
└─ 是 → 恢复状态,继续计数
|
||
```
|
||
|
||
**注意**:`GetTickCount64()` 在系统重启后会重置为 0,因此如果保存的时间戳大于当前时间,说明系统重启过,需要清除状态重新开始。
|
||
|
||
---
|
||
|
||
## 二、崩溃统计
|
||
|
||
### 2.1 记录内容
|
||
|
||
每次崩溃时记录以下信息(section: `[crash]`):
|
||
|
||
| 配置键 | 类型 | 说明 | 示例 |
|
||
|--------|------|------|------|
|
||
| `count` | int | 总崩溃次数 | `15` |
|
||
| `lastTime` | string | 最后崩溃时间 | `"2024-01-15 10:30:45"` |
|
||
| `lastCode` | string | 最后退出代码 | `"0xC0000005 (ACCESS_VIOLATION)"` |
|
||
| `lastRunMs` | string (uint64) | 最后运行时间(ms) | `"25000"` |
|
||
|
||
### 2.2 退出代码描述
|
||
|
||
常见的 Windows 异常代码:
|
||
|
||
| 代码 | 描述 |
|
||
|------|------|
|
||
| `0xC0000005` | ACCESS_VIOLATION(访问违规) |
|
||
| `0xC0000094` | INTEGER_DIVIDE_BY_ZERO(整数除零) |
|
||
| `0xC00000FD` | STACK_OVERFLOW(栈溢出) |
|
||
| `0xC0000409` | STACK_BUFFER_OVERRUN(栈缓冲区溢出) |
|
||
| `0xC000001D` | ILLEGAL_INSTRUCTION(非法指令) |
|
||
| `0x80000003` | BREAKPOINT(断点) |
|
||
|
||
正常退出:
|
||
|
||
| 代码 | 描述 |
|
||
|------|------|
|
||
| `0` | NORMAL(正常退出) |
|
||
|
||
自定义错误代码(1000-1999):
|
||
|
||
| 代码 | 描述 |
|
||
|------|------|
|
||
| `1001` | CONFIG_ERROR(配置错误) |
|
||
| `1002` | NETWORK_ERROR(网络错误) |
|
||
| `1003` | AUTH_FAILED(认证失败) |
|
||
| `1004` | RESTART_REQUEST(请求重启) |
|
||
| `1005` | MANUAL_STOP(手动停止) |
|
||
|
||
---
|
||
|
||
## 三、MTBF 统计
|
||
|
||
### 3.1 数据收集
|
||
|
||
| 配置键 | 类型 | 说明 |
|
||
|--------|------|------|
|
||
| `starts` | int | 启动次数 |
|
||
| `totalRunMs` | string (uint64) | 累计运行时间(ms) |
|
||
| `count` | int | 崩溃次数 |
|
||
|
||
**注意**:`totalRunMs` 和 `lastRunMs` 使用字符串存储 64 位整数,避免 32 位整数的限制(最大约 49 天)。
|
||
|
||
### 3.2 计算公式
|
||
|
||
**MTBF(平均故障间隔时间)**:
|
||
```
|
||
MTBF = 累计运行时间 / 崩溃次数
|
||
```
|
||
|
||
**失败率(Failure Rate)**:
|
||
```
|
||
失败率 = 崩溃次数 / 启动次数
|
||
```
|
||
|
||
### 3.3 示例
|
||
|
||
假设:
|
||
- 启动次数:100 次
|
||
- 崩溃次数:5 次
|
||
- 累计运行时间:30 天
|
||
|
||
计算结果:
|
||
- MTBF = 30 天 / 5 = 6 天(平均每 6 天发生一次崩溃)
|
||
- 失败率 = 5 / 100 = 5%(每次启动有 5% 概率崩溃)
|
||
|
||
---
|
||
|
||
## 四、回调函数设计
|
||
|
||
### 4.1 回调类型
|
||
|
||
| 回调 | 触发时机 | 参数 |
|
||
|------|---------|------|
|
||
| `onAgentStart` | 代理进程启动成功 | processId, sessionId |
|
||
| `onAgentExit` | 代理进程退出(任何原因) | exitCode, runtimeMs |
|
||
| `onCrash` | 检测到快速崩溃 | exitCode, runtimeMs |
|
||
| `onCrashWindowChange` | 崩溃窗口状态变化 | crashCount, firstCrashTime |
|
||
| `onCrashProtection` | 触发崩溃保护 | 无 |
|
||
|
||
### 4.2 调用顺序
|
||
|
||
正常退出:
|
||
```
|
||
onAgentExit(exitCode=0, runtime)
|
||
```
|
||
|
||
快速崩溃:
|
||
```
|
||
onAgentExit(exitCode, runtime)
|
||
↓
|
||
onCrash(exitCode, runtime)
|
||
↓
|
||
[如果 count >= 3]
|
||
onCrashProtection()
|
||
↓
|
||
onCrashWindowChange(count, startTime)
|
||
```
|
||
|
||
---
|
||
|
||
## 五、配置键汇总
|
||
|
||
Section: `[crash]`
|
||
|
||
| 配置键 | 类型 | 用途 | 持久性 |
|
||
|--------|------|------|--------|
|
||
| `count` | int | 总崩溃次数 | 永久 |
|
||
| `lastTime` | string | 最后崩溃时间 | 永久 |
|
||
| `lastCode` | string | 最后退出代码 | 永久 |
|
||
| `lastRunMs` | string | 最后运行时间 | 永久 |
|
||
| `totalRunMs` | string | 累计运行时间 | 永久 |
|
||
| `starts` | int | 启动次数 | 永久 |
|
||
| `winCount` | int | 窗口期崩溃次数 | 临时* |
|
||
| `winStart` | string | 窗口起始时间 | 临时* |
|
||
| `protected` | int | 崩溃保护标志 | 一次性** |
|
||
|
||
\* 临时:窗口期过期或系统重启后自动清除
|
||
\** 一次性:程序启动时读取并清除
|
||
|
||
---
|
||
|
||
## 六、文件改动
|
||
|
||
| 文件 | 改动内容 |
|
||
|------|---------|
|
||
| `CrashReport.h` | 配置键定义、退出代码常量、MTBF 计算函数 |
|
||
| `ServerSessionMonitor.h` | 回调类型定义、监控器结构体 |
|
||
| `ServerSessionMonitor.cpp` | 崩溃检测逻辑、回调调用 |
|
||
| `ServerServiceWrapper.cpp` | 回调实现、状态持久化/恢复 |
|
||
| `2015Remote.cpp` | 启动时检查崩溃保护标志 |
|
||
|
||
---
|
||
|
||
## 七、使用场景示例
|
||
|
||
### 场景 1:连续崩溃
|
||
|
||
```
|
||
T=00:00 代理启动 (startCount=1)
|
||
T=00:05 代理崩溃 (crashCount=1, 窗口开始)
|
||
T=00:10 代理重启 (startCount=2)
|
||
T=00:15 代理崩溃 (crashCount=2)
|
||
T=00:20 代理重启 (startCount=3)
|
||
T=00:25 代理崩溃 (crashCount=3, 触发保护!)
|
||
→ 服务停止重启代理
|
||
→ 写入 AgentCrashProtected=1
|
||
T=00:30 用户手动启动程序
|
||
→ 检测到保护标志,切换到普通模式
|
||
→ 弹出提示框
|
||
```
|
||
|
||
### 场景 2:服务重启后恢复
|
||
|
||
```
|
||
T=00:00 代理崩溃 (crashCount=1)
|
||
T=00:30 代理崩溃 (crashCount=2, 保存到注册表)
|
||
T=01:00 服务重启(非系统重启)
|
||
→ 加载状态: count=2, elapsed=1分钟
|
||
→ 仍在5分钟窗口内,恢复状态
|
||
T=01:30 代理崩溃 (crashCount=3, 触发保护!)
|
||
```
|
||
|
||
### 场景 3:窗口过期
|
||
|
||
```
|
||
T=00:00 代理崩溃 (crashCount=1)
|
||
T=02:00 代理崩溃 (crashCount=2)
|
||
T=08:00 代理崩溃
|
||
→ 距离第一次崩溃已超过5分钟
|
||
→ 重置窗口: crashCount=1, 新窗口开始
|
||
```
|
||
|
||
---
|
||
|
||
## 八、设计决策记录
|
||
|
||
| 问题 | 决策 | 理由 |
|
||
|------|------|------|
|
||
| 窗口期时长 | 5 分钟,不可配置 | 足够检测连续崩溃,避免误判 |
|
||
| 崩溃阈值 | 3 次,不可配置 | 平衡敏感度和容错 |
|
||
| 快速崩溃定义 | 30 秒 | 覆盖大多数初始化场景 |
|
||
| 系统重启处理 | 清除窗口状态 | 系统重启后问题可能已修复 |
|
||
| 统计数据重置 | 暂不实现 | 需求不明确,后续按需添加 |
|
||
| 日志记录 | 使用 Mprintf | 与现有日志系统一致 |
|
||
| 非零退出码 | 都算崩溃 | 正常退出必须返回 0 |
|
||
| lastRunMs 类型 | string (uint64) | 支持超过 24 天的运行时间 |
|