gcron 定时任务:Add/AddSingleton 与注意事项

结论:gcron 是框架内置的定时任务组件,注册 API 为 gcron.Add(ctx, cron表达式, job);表达式默认 6 段(秒 分 时 日 月 周)。AddSingleton 保证“上次未结束则不再次触发”,适合执行时长可能超过周期的任务;多实例部署时同一定时任务会各自执行,必须用分布式锁或选主保证只跑一份。

一、Cron 表达式(默认 6 段)

位置含义取值范围
第 1 段0-59
第 2 段0-59
第 3 段0-23
第 4 段1-31
第 5 段1-12
第 6 段0-6(0 或 7 表示周日)
  • 符号:* 任意、, 枚举、- 区间、*/n 步长、? 用于“日/周”互斥字段(具体规则以官方文档为准)。
  • 例 1:* * * * * * 每秒执行(调试用,勿上生产)。
  • 例 2:0 */5 * * * * 每 5 分钟执行。
  • 例 3:0 0 2 * * * 每天 02:00 执行。
  • 例 4:0 30 9 * * 1-5 工作日 09:30 执行。

二、注册方式对比

API行为典型适用
gcron.Add到点即触发,任务可并发重入执行快、可重入的任务
gcron.AddSingleton上次未结束则本次跳过同步、对账等不可重入长任务
gcron.AddOnce到点执行一次后移除一次性任务
gcron.AddTimes执行指定次数后停止有限次数重试类任务
  • 返回 *gcron.Entry 便于后续管理(启停/移除的具体 API 以官方文档为准)。
  • ctx 首参约定:与 g.Log 等组件一致,任务内继续向下传递 ctx 保持链路。

三、常见坑

  • 长任务重入:普通 Add 下任务执行超过周期会再次触发,可能重复扣款/重复推送 → 换 AddSingleton 或任务内加锁。
  • 多实例重复执行:gcron 是进程内调度,每个实例都会触发 → 必须加 Redis 分布式锁或做选主。
  • panic 兜底:任务回调内建议自行 recover,避免单个任务异常影响调度循环。
  • 时区语义:表达式按本地时区解析(以官方文档为准),跨时区部署需确认调度基准。
  • 注册时机:在 cmd/init 集中注册,任务体抽成独立函数便于手动触发与单元测试。

代码示例

// 每 5 分钟执行一次(6 段:秒 分 时 日 月 周)
entry, err := gcron.Add(ctx, "0 */5 * * * *", func(ctx context.Context) {
    doStat(ctx) // 统计类任务
})
if err != nil {
    g.Log().Fatal(ctx, err)
}

// 单例模式:上次未结束则本次跳过,防并发重入
gcron.AddSingleton(ctx, "0 */5 * * * *", func(ctx context.Context) {
    syncOrder(ctx) // 同步任务可能超过 5 分钟
})

// 每天 02:00 执行一次(AddOnce 示例)
_, err = gcron.AddOnce(ctx, "0 0 2 * * *", func(ctx context.Context) {
    doDaily(ctx)
})

// 任务管理:Entry 提供启停/移除等能力(API 以官方文档为准)
_ = entry

常见追问 / 记忆点

  • 追问:普通 Add 与 AddSingleton 如何选?→ 任务可能跑超周期且不可重入(扣款、推送、对账)用 AddSingleton;可重入短任务用 Add。
  • 追问:为什么本地正常、上生产会重复执行?→ 多副本部署时每个进程都有一套 gcron 调度,需分布式锁/选主保证唯一执行者。
  • 追问:表达式写不对常见原因?→ 少写“秒”段(把 5 位当 6 位用)、日与周同时指定冲突;先小步验证再上线。
  • 记忆点:6 段表达式、AddSingleton 防重入、分布式锁防多跑——“本地定时、全局去重”。
笔记加载中…