后台任务与多实例协调
除了处理 HTTP 请求,Sub2API 还运行多种周期任务和 worker。它们决定 token、订阅、统计、备份和异步任务能否长期保持健康。
主要后台任务
| 任务 | 作用 |
|---|---|
| Token Refresh | 提前刷新 OAuth token |
| Account Expiry | 到期账号自动暂停 |
| Proxy Expiry | 到期代理切换或恢复 |
| Subscription Expiry | 更新订阅状态和提醒 |
| Payment Order Expiry | 对账、关闭超时订单 |
| Usage Cleanup | 批量删除历史用量 |
| Idempotency Cleanup | 清理过期幂等记录 |
| Dashboard Aggregation | 生成仪表盘小时/日统计 |
| Ops Metrics/Aggregation | 采集系统指标和预聚合 |
| Ops Cleanup | 删除过期错误、指标和日志 |
| Ops Alert Evaluator | 根据规则生成告警事件 |
| Scheduled Reports | 生成周期运维报告 |
| Scheduler Snapshot | 更新账号调度快照 |
| Channel Monitor | 定时检测渠道模板与状态 |
| Scheduled Account Test | 执行账号测试计划 |
| Backup | 定时或手动数据库备份 |
| Batch Image Worker | 消费图片任务、轮询和结算 |
| Platform Quota Flusher | 批量回写用户平台配额 |
Leader Lock
多实例不能让每个副本都执行同一个全量任务。通用策略:
- 使用 Redis
SET NX获取带 owner 和 TTL 的 leader lock。 - 被其他实例持有时跳过本轮。
- Redis 出错时,部分任务回退 PostgreSQL advisory lock。
- 任务完成后 compare-and-delete 释放。
- 下一轮重新竞选,不永久绑定 leader。
TTL 必须大于任务最坏执行时间,否则锁执行中到期,另一个实例可能同时开始。
Heartbeat
Ops job heartbeat 用于区分:
- 任务正常执行但本轮无数据。
- 任务未被当前实例选为 leader。
- 任务卡死或长时间没有运行。
- 任务运行失败。
告警应根据计划周期设置阈值。例如每天一次的 cleanup 不能使用 5 分钟无心跳告警。
Token Refresh
周期刷新会扫描接近过期的 OAuth 账号,并且每个账号仍需要独立刷新锁。全局任务选主只能避免重复扫描,账号锁用于防止请求热路径与后台刷新并发。
监控:
- refresh request/success/failure。
- lock contention 和 wait time。
- OAuth 401 cooldown。
- refresh token rotation 后持久化失败。
过期任务
账号、代理、订阅和订单的“过期”含义不同:
- 账号:可以自动设为不可调度。
- 代理:可能切换 fallback proxy,并保留原 proxy ID 供恢复。
- 订阅:更新状态并发送提醒。
- 支付订单:先尝试 reconcile,再标记过期。
这些任务不应通过数据库定时器替代,因为它们还涉及缓存、通知和外部平台查询。
聚合与清理
直接在仪表盘扫描全部 usage_logs 会越来越慢。聚合任务生成小时、日表,清理任务按保留期删除原始或中间数据。
部署时要保证:
- 聚合已经覆盖将要删除的原始数据。
- cleanup 使用小批次,避免长事务和表膨胀。
- PostgreSQL 有 autovacuum 和磁盘余量。
- 任务失败有告警,不是静默跳过。
Batch Image Worker
批量图片任务包含 ready、delayed、active、inflight 和 lock 状态。Worker 需要处理:
- 新任务消费。
- 上游异步提交。
- 延迟轮询。
- worker 崩溃后的 stale active 恢复。
- 余额冻结、结算和释放。
- 输入输出保留与清理。
启用该功能后,Redis 队列和对象存储不再是可忽略的附属依赖。
Worker Pool 与应用退出
用量、邮件、批量任务等 worker 在应用退出时需要 Stop/StopAndWait。部署平台要提供足够 termination grace period,让:
- 停止接受新请求。
- 取消周期任务。
- 等待关键用量任务完成。
- 释放数据库和 Redis 连接。
直接 SIGKILL 会中断尚未落库的内存任务。
多实例配置要求
- 所有副本使用相同 Redis 和 PostgreSQL。
- 系统时间和时区一致。
- leader lock key 不被不同环境共用。
- staging 与 production 必须使用不同 Redis DB/前缀和数据库。
- 每个实例有唯一 instance ID。
- 总数据库连接池按实例数计算。
运维检查
每天或通过告警观察:
- job heartbeat 是否更新。
- leader lock 是否长期不释放。
- scheduler outbox lag/backlog。
- usage worker waiting/dropped/sync fallback。
- quota flusher dirty/readd/error。
- cleanup 删除数量与耗时。
- batch queue ready/delayed/active 数量。
- token refresh 失败账号数量。