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 防重入、分布式锁防多跑——“本地定时、全局去重”。