生产项目必须用 github.com/robfig/cron/v3,需手动启用秒级、时区、panic 捕获;常见不执行原因是未调 c.start() 或 main 退出过快;默认 utc 时区,须显式传 cron.withlocation(time.local);panic 不恢复会导致调度器停摆;秒级需 cron.withseconds() 且用 6 段表达式。

别用 github.com/robfig/cron(v1/v2),它已归档、不维护、Go 1.20+ 下常报 undefined: time.ParseInLocation;生产项目必须用 github.com/robfig/cron/v3,且秒级、时区、panic 捕获都得手动开,否则默认行为和你预期差很远。
为什么 cron.New() 后任务根本不执行
最常见原因是没调 c.Start(),或者调了但 main 函数立刻退出。v3 的 c.Start() 是异步启动 goroutine,不阻塞当前线程,所以 select{} 或信号监听必须跟上。
- 漏掉
c.Start():实例只是空壳,AddFunc注册完也不会触发 - 写了
c.Start()但没阻塞 main:进程秒退,goroutine 被强制终止 - 用
go c.Start()却没配 signal 监听:无法优雅退出,defer c.Stop()可能不执行 - 表达式语法错误(如字段数不对):
AddFunc静默失败,不 panic 也不报错,只打 warning 日志(默认不输出)
验证方式:调 c.Entries(),返回空 slice 说明表达式没被成功解析。
如何让 "0 0 * * *" 真正在本地零点跑(不是 UTC 零点)
v3 默认所有时间计算都走 time.UTC,哪怕你机器 TZ=Asia/Shanghai 也无效。写 "0 0 * * *" 实际在 UTC 0 点(北京时间早 8 点)触发。
- 必须显式传
cron.WithLocation(time.Local)初始化:cron.New(cron.WithLocation(time.Local)) - 别用
time.LoadLocation("Asia/Shanghai"):容器环境常缺tzdata,time.Local更可靠 - 验证是否生效:打印
c.Entry().Schedule.Next(time.Now()),看小时数是否符合本地预期 - 注意 Docker:基础镜像要装
tzdata并设TZ=Asia/Shanghai,否则time.Local会 fallback 到 UTC
为什么定时任务 panic 一次,整个调度器就停摆
cron/v3 默认不 recover 任何 panic —— 一个任务空指针或除零,c.Run() 流程直接退出,后续所有任务永久失效,且无明确日志提示。
- 最简修复:每个
AddFunc内部包一层defer func(){ if r := recover(); r != nil { log.Printf("cron job panic: %v", r) } }() - 统一处理:用
cron.New(cron.WithChain(cron.Recover(cron.DefaultLogger))),但它只捕获 panic,不处理函数返回的error - 别在
AddFunc里反复创建 DB 连接或 HTTP 客户端:资源泄漏 + panic 风险双叠加 -
AddFunc和AddJob都不是 goroutine-safe:禁止在任务里动态调Remove或AddFunc
需要每 5 秒跑一次,但 "*/5 * * * * *" 总是不准或堆积
v3 默认只支持 5 段表达式(分、时、日、月、周),"*/5 * * * * *" 会被静默截断成 "* * * * *",变成每分钟执行一次。秒级必须显式启用,且字段数必须匹配。
- 初始化加
cron.WithSeconds():cron.New(cron.WithSeconds()) - 表达式必须为 6 段:
"*/5 * * * * *"(秒 分 时 日 月 周) - 别在
AddFunc里time.Sleep(5)模拟秒级:会漂移、堆积、不可控 - 真正高精度、低延迟场景(如毫秒级或 >100 个任务),
cron/v3底层靠遍历判断,性能下降明显,应改用time.Ticker手写
复杂点在于:时区、秒级、panic 恢复、资源复用这四件事必须同时做对,少一个线上就静默故障;而它们又分散在初始化、注册、执行三个阶段,容易只改一处忘了其他。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











