cron.New() 默认用 UTC 时区且不捕获 panic,导致任务时间错乱、静默失败;需显式传 cron.WithLocation(time.Local) 并加 cron.WithChain(cron.Recover(cron.DefaultLogger))。

直接用 cron.New() 启动定时任务,大概率会静默失败:任务不触发、时间错乱、panic 一崩全停——不是代码写错了,是默认配置根本没适配真实运行环境。
为什么 cron.Start() 调了但任务从不执行
根本原因是 c.Start() 只启动一个后台 goroutine,它不阻塞 main()。main 函数一返回,整个进程退出,所有 goroutine 被强制终止。
- ❌ 错误写法:
c.Start()放在main()最后一行,后面没任何等待逻辑 - ✅ 正确做法:用
signal.Notify监听SIGINT或SIGTERM,然后阻塞住 main - ⚠️ 注意:
c.Start()在 v3 中已是异步启动,不要再套go c.Start(),否则可能重复启 goroutine 导致竞态
"0 0 * * *" 为什么在北京时间上午 8 点才跑
因为 cron.New() 默认用 time.UTC 解析表达式,哪怕你机器 TZ=Asia/Shanghai 也无效。你写的“每天 0 点”,实际按 UTC 时间触发(即北京时间上午 8 点)。
- ✅ 必须显式传
cron.WithLocation(time.Local);生产环境更稳可用time.FixedZone("CST", 8*60*60) - ❌ 不要硬编码
time.LoadLocation("Asia/Shanghai")——容器里常缺 tzdata,time.Local更可靠(但需确保宿主机时区配置正确) - ? 验证方式:调
c.Entry().Schedule.Next(time.Now()),看返回时间的小时字段是否符合预期(比如当前 23:59,下一触发应为次日 00:00)
秒级任务必须配 cron.WithSeconds(),且表达式必须是 6 段
v3 默认只认 5 段表达式(分、时、日、月、周)。写成 "* * * * * *" 却没传 cron.WithSeconds(),解析会静默失败——AddFunc 不报错也不注册任务。
- ✅ 启用秒级:初始化时加
cron.WithSeconds(),且表达式必须是 6 段(秒、分、时、日、月、周) - ❌ 不要混用:传了
WithSeconds()却写"0 * * * *"(5 段),任务照样不注册 - ⚠️ 表达式空格敏感:
"* * * * * *"合法,"* * * * * *"(双空格)会解析失败
AddFunc 里 panic 会导致整个调度器停摆
v3 默认串行执行所有 job,且无 panic 捕获机制。一个任务空指针或除零,c.Run() 流程直接退出,后续所有任务永久失效,还不打日志。
- ✅ 推荐方案:初始化时加
cron.WithChain(cron.Recover(cron.DefaultLogger)),它会在每个任务外层自动包一层defer recover - ⚠️ 注意:它只捕获 panic,不处理函数返回的
error;业务错误仍需主动判断和记录 - ? 避免闭包捕获循环变量:
for i := range jobs { c.AddFunc("", func() { use(i) }) }——所有任务最终都用最后一个i值;应改用显式参数传入:func(i int) { use(i) }(i)
最易被忽略的是时区和 panic 捕获这两项——它们不报错、不中断编译、不提示缺失,但会让任务在生产环境里“看起来正常启动”,实则完全不可靠。别等上线后查日志才发现凌晨任务全在上午八点执行。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











