
Go cron 任务 panic 会导致整个调度器停止吗
会。标准库 github.com/robfig/cron/v3(最常用)默认不 recover 任何 panic,只要一个任务 panic,对应 goroutine 崩溃,但调度器本身还能继续触发后续任务——**前提是 panic 没波及到调度器主 goroutine**。不过实际中常见的是:panic 后日志没打、错误没捕获、重试逻辑缺失,导致你以为“任务还在跑”,其实已经静默失败。
- 默认行为下,
cron.FuncJob或cron.Job实现里抛出 panic,只会终止该次执行,不影响下次调度 - 但如果在
cron.New()时传了自定义cron.Option(比如cron.WithChain(cron.Recover(cron.DefaultLogger))),它才会自动 recover 并记录 - 没配 recover 时,panic 错误根本不会输出到标准日志,容易误判为“任务没运行”
用 cron.WithChain(cron.Recover(...)) 捕获异常但没日志怎么办
因为 cron.Recover 默认只调用 logger 的 Errorf 方法,而 cron.DefaultLogger 是个空实现——它什么也不输出。你看到“没日志”,不是配置失效,是 logger 没干活。
- 必须显式传一个真正能打日志的 logger,比如
log.New(os.Stderr, "[cron] ", log.LstdFlags) - 或者用结构体实现
cron.Logger接口,确保Errorf方法写入文件或上报 Sentry - 别直接用
cron.WithLogger(...)替代WithChain:前者只控制调度器自身日志(如“added job”),不处理 job 执行异常
logger := log.New(os.Stderr, "[cron] ", log.LstdFlags)
c := cron.New(cron.WithChain(
cron.Recover(logger),
))
任务执行超时后怎么中断并标记失败
cron 本身不提供超时控制,得靠任务内部加 context。否则 HTTP 请求卡住、数据库锁死,任务会一直 hang,占满 goroutine,还可能堆积未执行任务。
- 不要依赖 cron 的 “每分钟跑一次” 就等于“每次都能快速结束”——必须自己控制执行生命周期
- 在 job 函数里用
context.WithTimeout包裹耗时操作,超时后主动 return,避免阻塞 - 超时 error 不会自动被
cron.Recover捕获(它只抓 panic),所以要在代码里显式记录,比如logger.Errorf("task timeout: %v", err) - 注意:cron v3 的
Next调度不受影响,但若某次执行耗时 > 间隔时间,下一次会在前一次结束后立即触发(即“追赶模式”),可能引发并发冲突
为什么 Sentry / Prometheus 看不到 cron 任务失败指标
因为 cron 不发指标,也不自动上报异常——所有可观测性都要你自己插桩。Sentry 不会自动捕获 job 里的 panic,Prometheus 也不会自动暴露“某 job 上次失败时间”这类指标。
立即学习“go语言免费学习笔记(深入)”;
- 在 recover 日志之后,立刻调用
sentry.CaptureException(err)(如果用了 sentry-go) - 用
prometheus.CounterVec自增job_failures_total{job="sync_user"},并在 recover 分支里 +1 - 别只在 panic 路径上报:也要在业务错误路径(如
if err != nil && !errors.Is(err, context.DeadlineExceeded))里上报,否则超时失败就漏监控 - 注意计数器 label 值别带动态内容(比如用户 ID),防止 cardinality 爆炸










