异步任务与可靠性

为什么采用异步处理

对象存储中的上传、物理删除、回源、审核和备份可能持续数秒到数小时。Cloud Blox 将“请求已可靠接收”和“最终副作用已完成”分开:

  • 请求阶段尽快完成校验和持久化;
  • 耗时工作由后台消费者执行;
  • 失败任务保留状态并重试;
  • 运维人员可以查看、取消或重新执行任务;
  • 多区域部署可以把任务路由到数据所在区域。

七类业务队列

当前业务队列包括:

  • 上传:把临时文件写入最终存储通道;
  • 审核:执行内容审核,通过后继续上传;
  • 删除:递减引用并在满足条件时删除物理文件;
  • 副本:把文件复制到冗余通道;
  • 回源:从外部源站拉取内容并进入存储流程;
  • 备份:把对象复制到存储桶备份目标;
  • 恢复:从备份目标恢复主通道缺失内容。

队列数量与能力会随发行版变化。复制和移动属于对象操作,不应与独立业务队列数量混淆。

可靠投递模型

stateDiagram-v2
    [*] --> Pending: 业务事务保存任务
    Pending --> Dispatching: 认领投递权
    Dispatching --> Dispatched: 已进入 Redis 队列
    Dispatching --> Pending: 投递失败并安排重试
    Dispatched --> Running: Worker 取得执行租约
    Running --> Completed: 副作用和状态均完成
    Running --> Pending: 可重试失败
    Running --> Failed: 不可重试或超过上限
    Running --> Cancelled: 用户取消
    Running --> Pending: 执行租约过期后恢复

核心原则是:

  • MySQL 中的任务记录代表可靠业务意图;
  • Redis 队列负责及时唤醒消费者;
  • 即时投递失败不会删除业务意图;
  • 定时扫描会补投到期或卡住的任务;
  • 重复投递通过稳定任务标识和状态认领消解;
  • Worker 必须先取得执行所有权,再产生副作用。

执行租约与故障恢复

任务开始执行后会持有带所有者的执行租约,并周期续租:

  • 正常完成后写入终态;
  • 进程崩溃或节点失联后,租约到期;
  • 其他消费者可以恢复并重新认领;
  • 续租失败时应终止当前执行,避免两个节点继续产生副作用;
  • 任务结果写入失败时会重试或使用降级更新,避免长期停留在“执行中”。

对于上传和回源,重试还会考虑多个通道:当前通道失败后可以切换其他候选通道,直到所有通道达到各自重试上限。

幂等边界

任务投递

相同持久任务重复投递不会创建第二个业务任务。手动重试会生成新的队列投递标识,但仍关联原任务记录。

上传

  • 对象和上传任务意图一起保存;
  • 相同内容支持秒传;
  • 覆盖同名对象会取消被替代的待执行上传;
  • 客户端分片上传使用稳定操作标识,可恢复已完成分片和最终结果。

删除

  • 同一删除任务重试不会重复递减引用;
  • 物理文件删除前需要取得删除所有权;
  • 删除完成、重试和失败分别持久化;
  • 只有引用归零且删除条件满足时才删除物理文件。

计费

  • 每个计费事件有唯一标识;
  • 分析存储写入成功但确认失败时允许重放;
  • 日结算按用户和账期防重复;
  • 账单、扣费和钱包流水在同一事务中完成。

区域队列

区域相关任务会进入带区域标识的队列。节点只消费自己负责的区域,同时保留基础队列作为无区域任务和兼容场景的兜底。

运维需要确保:

  • 存储桶区域配置与节点端点一致;
  • 每个活跃区域至少有健康消费者;
  • 基础队列没有持续积压;
  • 区域下线前迁移或清空其待执行任务;
  • 节点标签、区域名称和队列名称在监控中可对应。

计划任务

当前版本最多包含十五项内置计划任务,部分任务受发行版和配置开关控制。

业务与计费

  • 小时用量快照;
  • 日结算;
  • 计费事件投递;
  • 资源包过期。

存储与对象

  • 统计刷新;
  • 临时文件清理;
  • 对象到期删除;
  • 副本备份;
  • 数据迁移;
  • 待执行任务补投。

运维与数据治理

  • 区域健康检查;
  • 节点指标采集;
  • ClickHouse 日志保留策略同步;
  • MySQL 日志清理;
  • MySQL 与 ClickHouse 数据备份。

分布式调度

全局任务使用 Redis 租约避免多节点重复执行。关键财务和状态任务在无法取得锁时应停止执行,统计类任务可以根据风险选择降级策略。区域任务由负责该区域的节点执行。

多节点部署必须避免把“主节点标识”当作唯一防重手段。运维验收应模拟:

  • 两个节点同时触发同一任务;
  • Redis 短暂不可用;
  • 持锁节点执行中崩溃;
  • 任务执行超过初始锁时长;
  • 手动执行与自动调度重叠。

关闭顺序

应用退出时按以下顺序收敛:

  1. 停止接收新 HTTP 流量;
  2. 取消应用生命周期;
  3. 等待计划任务和后台任务退出;
  4. 等待队列消费者停止;
  5. 关闭缓存、队列和 Redis 连接;
  6. 排空日志并关闭 ClickHouse 和 MySQL。

每个阶段都需要超时上限,防止外部存储或队列异常使进程永久无法退出。

运维观察重点

  • 各队列等待、执行、重试和失败数量;
  • 最老待执行任务年龄;
  • 任务投递重试和执行租约回收次数;
  • Worker 数量与当前并发;
  • 各区域是否有消费者;
  • 上传临时目录使用率;
  • 计费事件积压与最老事件年龄;
  • 小时快照完整率和结算水位;
  • 计划任务最近成功时间、持续时间和连续失败次数。

已知运行边界

  • Redis 是异步上传和可靠队列的关键依赖,不能只按“可选缓存”对待。
  • 默认单应用实例使用本地临时文件;水平扩容前需要处理会话持久化、共享临时存储或请求粘性。
  • 浏览器断点续传在应用重启和节点切换场景需要额外的持久化保障。
  • 长时间外部存储操作必须同时受任务超时、网络超时和关闭信号约束。

相关数据语义见数据与对象生命周期,探针、指标与告警建议见安全与可观测性。