亮点系统可靠性
系统可靠性
多实例故障转移、任务可观测性、健康检查与自动重试——确保 OctoReport 在生产环境稳定运行。
基于多实例故障转移、任务可观测性、健康检查的高可用系统架构,确保服务稳定运行。
💡 核心目标:99.9% 服务可用性,自动故障恢复,完整的任务追踪能力。
1. 多实例故障转移
1.1 RSSHub 多实例架构
架构设计:
- 支持配置多个 RSSHub 实例(官方实例 + 自建实例)
- 每个实例独立健康检查
- 自动故障转移(主实例失败 → 备用实例)
- 实例优先级排序(自建 > 官方 rsshub.app)
实例配置示例:
管理后台 → RSSHub 实例管理
实例列表:
┌─────────────────────────┬──────────┬────────┬──────────┐
│ 实例 URL │ 优先级 │ 状态 │ 认证模式 │
├─────────────────────────┼──────────┼────────┼──────────┤
│ https://my-rsshub.com │ 1(最高)│ 健康 │ BEARER │
│ https://rsshub.app │ 2 │ 健康 │ NONE │
│ https://backup.rsshub.com│ 3 │ 异常 │ KEY │
└─────────────────────────┴──────────┴────────┴──────────┘1.2 故障转移流程
自动转移机制:
- 请求主实例(优先级最高的健康实例)
- 检测失败(超时、500 错误、连接失败)
- 标记实例异常(降低优先级或暂时禁用)
- 尝试下一个实例(按优先级顺序)
- 记录转移日志(审计追踪)
流程示例:
时间线:
10:00:00 - 用户创建 RSS 数据源
• 选择实例:my-rsshub.com(优先级 1)
• 请求 URL:https://my-rsshub.com/bilibili/user/video/123
• 结果:✅ 成功(200 OK)
11:00:00 - 定时任务执行
• 选择实例:my-rsshub.com(优先级 1)
• 请求 URL:https://my-rsshub.com/bilibili/user/video/123
• 结果:❌ 失败(Connection Timeout)
• 操作:
1. 标记 my-rsshub.com 为"异常"
2. 切换到备用实例 rsshub.app(优先级 2)
3. 请求 URL:https://rsshub.app/bilibili/user/video/123
4. 结果:✅ 成功(200 OK)
• 日志:"Failover from my-rsshub.com to rsshub.app"
12:00:00 - 健康检查恢复
• 检测到 my-rsshub.com 已恢复
• 恢复其优先级
• 下次执行时再次使用 my-rsshub.com1.3 健康检查机制
检查方式:
- 主动检查:每 5 分钟向实例发送测试请求
- 被动检查:用户请求失败时立即标记
- 恢复检查:异常实例每 10 分钟尝试恢复
检查指标:
| 指标 | 健康阈值 | 异常阈值 |
|---|---|---|
| 响应时间 | < 5 秒 | > 10 秒 |
| 错误率 | < 5% | > 20% |
| 连接成功率 | > 95% | < 80% |
2. 任务可观测性
2.1 完整的任务日志
日志内容:
id: 任务唯一IDtype: 任务类型(COLLECT, CLEAN, REPORT_GENERATE)status: 状态(PENDING, PROCESSING, SUCCESS, FAILED)createdAt: 创建时间startedAt: 开始时间completedAt: 完成时间duration: 执行时长(毫秒)creditsUsed: 积分消耗message: 执行消息error: 错误信息(如果失败)
日志查看:
侧边栏 → 任务日志
支持的筛选:
• 按类型:数据收集 / 内容清洗 / 报告生成
• 按状态:全部 / 成功 / 失败 / 进行中
• 按时间:最近 24 小时 / 最近 7 天 / 最近 30 天
• 按数据源:选择特定数据源
• 按报告:选择特定报告模板
示例日志:
┌──────────────────┬────────┬────────┬────────┬──────────┐
│ 时间 │ 类型 │ 状态 │ 时长 │ 积分消耗 │
├──────────────────┼────────┼────────┼────────┼──────────┤
│ 10:00:15 │ 数据收集│ 成功 │ 2.3s │ 20 │
│ 10:05:30 │ 报告生成│ 成功 │ 45s │ 150 │
│ 10:10:00 │ 数据收集│ 失败 │ 0.5s │ 0 │
│ 10:15:45 │ 问答 │ 成功 │ 3s │ 15 │
└──────────────────┴────────┴────────┴────────┴──────────┘
点击任务查看详情:
{
"id": "task_abc123",
"type": "COLLECT",
"status": "FAILED",
"message": "Connection timeout",
"error": "Failed to connect to rsshub.app after 3 retries",
"startedAt": "2025-10-27T10:10:00Z",
"duration": 500,
"creditsUsed": 0
}2.2 实时状态监控
任务状态机:
PENDING(待执行)
↓
PROCESSING(执行中)
↓
SUCCESS(成功) / FAILED(失败)
状态转换规则:
• PENDING → PROCESSING:Worker 开始处理
• PROCESSING → SUCCESS:执行成功
• PROCESSING → FAILED:执行失败(超时/错误/余额不足)
• 不允许状态回退(单向流转)进行中的任务:
- 任务日志中显示"进行中"标签
- 实时更新执行时长
- 支持查看 Worker 日志(高级功能)
2.3 性能指标追踪
关键指标:
| 指标 | 正常范围 | 异常阈值 | 影响 |
|---|---|---|---|
| 数据收集时长 | 1-5 秒 | > 30 秒 | 数据源响应慢 |
| 报告生成时长 | 10-60 秒 | > 5 分钟 | LLM 响应慢或内容过多 |
| 任务成功率 | > 95% | < 80% | 配置错误或服务异常 |
3. 故障恢复机制
3.1 自动重试策略
重试场景:
- ✅ 网络超时(Timeout)
- ✅ 临时服务不可用(503 Service Unavailable)
- ✅ 速率限制(429 Too Many Requests)
- ❌ 配置错误(404 Not Found,不重试)
- ❌ 认证失败(401 Unauthorized,不重试)
重试策略:
指数退避重试(Exponential Backoff)
第1次失败 → 等待 1 秒 → 重试
第2次失败 → 等待 2 秒 → 重试
第3次失败 → 等待 4 秒 → 重试
第4次失败 → 放弃,标记任务失败
最大重试次数:3 次
总超时时间:30 秒
示例日志:
2025-10-27 10:00:00 [INFO] Attempting request (1/3)
2025-10-27 10:00:05 [WARN] Timeout, retrying in 1s (2/3)
2025-10-27 10:00:08 [WARN] Timeout, retrying in 2s (3/3)
2025-10-27 10:00:15 [ERROR] Max retries exceeded, task failed3.2 降级策略
服务降级场景:
- Firecrawl 不可用 → 自动降级到 Browserless
- 主 LLM 模型不可用 → 切换到备用模型(管理员配置)
- RSSHub 实例不可用 → 切换到其他实例
降级示例(网页抓取):
scrapePageDetail() 函数的降级流程:
1. 尝试 Firecrawl(首选)
↓ 失败(超时/API错误)
2. 记录日志:"Firecrawl failed, falling back to Browserless"
↓
3. 尝试 Browserless(备用)
↓ 成功
4. 返回结果 + 标注使用的提供商
任务日志中显示:
{
"provider": "browserless",
"fallback": true,
"reason": "firecrawl_timeout"
}3.3 手动干预能力
管理员操作:
- 禁用异常实例(RSSHub 实例管理 → 禁用实例)
- 手动重试任务(任务日志 → 点击"重试")
- 调整优先级(RSSHub 实例管理 → 修改优先级)
用户操作:
- 手动触发执行(数据源/报告列表 → "立即执行")
- 查看失败原因(任务日志 → 查看错误详情)
- 修改配置后重试(编辑数据源/报告 → 保存 → 立即执行)
4. 数据一致性保障
4.1 事务保护
关键操作使用事务:
- ✅ 积分扣费 + 任务创建(原子操作)
- ✅ 内容保存 + 去重处理(原子操作)
- ✅ 报告生成 + 步骤结果保存(原子操作)
事务示例:
// 内容保存 + 去重
await prisma.$transaction(async (tx) => {
// 1. 查找重复 URL
const existing = await tx.content.findFirst({
where: { sourceUrl: url }
})
// 2. 如果存在,标记旧内容为过期
if (existing && strategy === 'UPDATE') {
await tx.content.update({
where: { id: existing.id },
data: { isExpired: true, expiredAt: new Date() }
})
}
// 3. 保存新内容
return await tx.content.create({
data: { sourceUrl: url, title, content }
})
})
// 保证:要么全部成功,要么全部失败(不会出现半成功状态)4.2 并发控制
防止数据竞争:
- 乐观锁:使用版本号控制(
version字段) - 悲观锁:关键操作使用
FOR UPDATE(如积分扣费) - 唯一约束:数据库层面防止重复(如
sourceUrl索引)
并发场景示例:
用户同时提交 2 个报告生成任务:
任务 A:
1. 查询积分余额:1000
2. 扣除 200 → 余额 800
3. 创建报告 A
4. 提交事务 ✅
任务 B:
1. 查询积分余额:800(任务 A 已扣费)
2. 扣除 200 → 余额 600
3. 创建报告 B
4. 提交事务 ✅
结果:两个任务都成功,余额正确(600)
如果没有事务保护:
任务 A 和 B 同时查询余额 → 都是 1000
任务 A 扣费 → 余额 800
任务 B 扣费 → 余额 800(错误!应该是 600)
结果:积分不一致 ❌5. 系统监控指标
5.1 关键性能指标(KPI)
| 指标 | 目标值 | 当前值 | 监控方式 |
|---|---|---|---|
| 系统可用性 | > 99.9% | 99.95% | 健康检查 |
| 任务成功率 | > 95% | 97.3% | 任务日志统计 |
| 平均响应时间 | < 3 秒 | 2.1 秒 | 性能追踪 |
| 数据一致性 | 100% | 100% | 事务审计 |
5.2 告警机制
告警触发条件:
- ✅ 任务成功率 < 80%(1 小时内)
- ✅ RSSHub 实例全部不可用
- ✅ 数据库连接池耗尽
- ✅ Redis 连接失败
告警通知:
- 管理员邮件通知
- 管理后台红色警告标识
- 系统日志记录
6. 用户体验保障
6.1 透明的错误提示
用户友好的错误消息:
| 技术错误 | 用户看到的提示 |
|---|---|
Connection timeout | 网络连接超时,请稍后重试 |
Insufficient credits | 积分不足,请充值后重试 |
Invalid RSSHub route | RSS 路由配置错误,请检查路由格式 |
Rate limit exceeded | API 调用频率过高,请 5 分钟后重试 |
6.2 自助排查工具
提供的工具:
- 任务日志:查看详细的执行日志和错误信息
- 消费明细:追踪积分消耗和退款记录
- 健康状态:查看系统组件状态(未来功能)
- 文档搜索:快速查找常见问题解决方案
7. 常见问题
Q1: 如果所有 RSSHub 实例都不可用怎么办?
A:系统会:
- 任务标记为失败
- 自动退款(如果已扣费)
- 发送告警通知给管理员
- 建议:配置多个实例(官方 + 自建)降低风险
Q2: 任务一直处于"进行中"状态怎么办?
A:可能原因:
- Worker 进程异常:联系管理员检查 Worker 状态
- 任务确实在执行:复杂报告可能需要 5-10 分钟
- 超时未检测:系统会在 30 分钟后自动标记超时
Q3: 为什么有时候任务会自动重试?
A:系统会在以下情况自动重试:
- 网络超时(Timeout)
- 临时服务不可用(503)
- 速率限制(429,等待后重试)
不会重试的情况:
- 配置错误(404, 401)
- 积分不足(Insufficient credits)
- 业务逻辑错误(Invalid data)
Q4: 如何查看系统的健康状态?
A:当前查看方式:
- 任务日志:查看最近的任务成功率
- RSSHub 实例管理(管理员):查看实例状态
- 未来功能:系统状态页(显示所有组件健康状况)