Skip to content

周期任务与定时轮询 ​

周期任务按时间规则产生执行实例。定时轮询则反复查询某个状态,例如每隔 10 秒扫描到期订单。一次周期执行可以领取多条业务任务,两者的执行粒度不同。

固定间隔与日历规则 ​

规则示例需要明确的语义
固定频率每隔 60 秒开始一轮上一轮未结束时是否重叠
完成后等待上一轮结束 60 秒后开始耗时会让实际开始时间逐渐后移
日历规则上海时间每天 02:00时区、日历边界和错过后的处理

“每天”与“每隔 24 小时”在涉及夏令时的地区可能产生不同结果。日历计划应保存 IANA 时区,例如 Asia/Shanghai;执行实例同时保存解析后的计划时间,便于追溯当时应该执行哪一轮。

下面使用 Kubernetes v1.27 起稳定可用的时区配置方式;该特性在 v1.24 为 Alpha,v1.25–v1.26 为默认启用的 Beta,参见官方特性门控历史。其他字段含义见 CronJob 文档。镜像是示例占位,需要替换为自己的报表程序。

yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: daily-sales-report
spec:
  schedule: "0 2 * * *"
  timeZone: "Asia/Shanghai"
  concurrencyPolicy: Forbid
  startingDeadlineSeconds: 1800
  jobTemplate:
    spec:
      backoffLimit: 2
      template:
        spec:
          restartPolicy: Never
          containers:
            - name: report
              image: example.invalid/sales-report:replace-me

这里禁止同一个 CronJob 的活跃 Job 重叠,并允许在计划时间后 30 分钟内补启动。该并发策略不协调其他 CronJob,也不提供业务去重;任务处理逻辑仍要识别相同的报表日期。

调度计划、实例与重试 ​

假设销售报表每天 02:00 执行:

  • 计划保存“每天 02:00、上海时区、处理前一天数据”。
  • 2026-10-27 02:00 对应一轮执行实例,其数据窗口是前一天。
  • 这一实例第一次失败后,第二次尝试仍处理相同数据窗口。

实例可以用 schedule_id + scheduled_at 建唯一约束。报表写入也需要按日期或窗口覆盖、合并或版本化,避免同一轮重试后产生两份可见结果。

补跑历史报表时,业务日期应来自实例参数。直接使用 Worker 启动时的“昨天”,会让延迟多天的任务处理错误日期。

上一轮没有完成 ​

策略行为代价
允许重叠新实例按计划启动下游并发增加,可能竞争同一数据
跳过本轮活跃实例结束前不再启动必须记录漏掉的窗口
排队执行保存新实例,等待资源空闲任务可能落后很多轮
合并执行将多个待处理窗口合并需要业务允许合并,保留窗口边界

对需要完整覆盖数据窗口的对账任务,可以保留实例再限流执行;对只关心最新状态的缓存刷新,合并旧轮次可能更合适。中止旧进程也不能撤回它已经提交的写入。

错过执行后的补跑 ​

系统停机三小时后恢复,计划可能已经错过多轮。补跑需要同时限定时间窗口、最多生成多少实例,以及是否沿用原来的业务日期。

Kubernetes 官方说明 CronJob 的调度是近似的,特定情况下可能重复创建或漏建 Job,因此任务需要幂等。它对补跑也有自身的检查规则,不能把某种调度器的行为当成所有定时系统的默认承诺。参见 Job creation。

轮询间隔与扫描成本 ​

假设到期时间相对扫描周期均匀分布,扫描每 10 秒执行一轮,且没有积压、暂停和查询耗时,单纯等待下一轮扫描的时间平均约 5 秒。真实开始延迟还包括查询、领取、排队和 Worker 等待,不能把 10 秒间隔当成严格上界。

轮询频率提高前,先检查查询是否只扫描“未完成且已到期”的索引范围。分页扫描应保存推进位置或不断领取状态发生变化的批次,避免用大 OFFSET 重复跳过前面的数据。

兜底扫描还要有修复目标。例如扫描“到期但没有关闭检查任务的订单”,补建缺失任务;仅重复扫描任务表,发现不了从未创建过的任务。

参考资料 ​