Go 中如何运行定时(cron)任务?
在 Go 程序里跑 cron 定时任务:用 robfig/cron 库注册表达式调度,含安装、AddFunc 示例、带秒的六字段格式、time.Ticker 替代方案与 FAQ。
Go 标准库没有 cron 表达式调度器,社区事实标准是 robfig/cron:import 后 cron.New() 创建调度器,AddFunc("表达式", 函数) 注册任务,Start() 启动。 只要进程活着,任务就按点执行。
1. 安装
go get github.com/robfig/cron/v3
2. 最小示例:每分钟打印一次
package main
import (
"fmt"
"github.com/robfig/cron/v3"
)
func main() {
c := cron.New()
// 注册任务:标准五字段表达式
c.AddFunc("* * * * *", func() {
fmt.Println("Hello world!")
})
c.Start()
// 阻塞主 goroutine,实际项目中用 select{} 或等退出信号
select {}
}
表达式与系统 crontab 一致:分 时 日 月 星期。如每天凌晨 3 点半:"30 3 * * *"。
3. 需要秒级精度
v3 默认五字段;要六字段(带秒)就显式开解析器:
c := cron.New(cron.WithSeconds())
c.AddFunc("*/10 * * * * *", func() { fmt.Println("每 10 秒") })
4. 常用进阶项
- 描述性调度:
c.AddFunc("@every 5m", fn)、@daily、@hourly等宏。 - 带参数任务:
c.AddJob("0 * * * *", myJob{client: db}),实现Run()方法的类型。 - 防重叠:
cron.New(cron.WithChain(cron.SkipIfStillRunning(logger))),上一轮没跑完就跳过。 - 指定时区:表达式前加
CRON_TZ=Asia/Shanghai,或cron.WithLocation(loc)。
5. 简单场景可以不上库
固定间隔的轮询用标准库 time.Ticker 就够了:
ticker := time.NewTicker(5 * time.Minute)
for range ticker.C {
doWork()
}
但它表达不了"每天凌晨 2 点"这种日历语义——那才是 cron 库的价值。
观测云对照
进程内调度器绕过了系统 cron,意味着 /var/log/cron 里查不到它——任务跑没跑全靠应用自己说。Go 服务接入观测云 APM 后,定时任务的关键路径可以埋点成 span;任务日志统一采集后配心跳式监控器:计划时间窗口内没有成功日志就告警,进程挂了、调度器 panic、任务卡死三种情况都能覆盖。
常见问题(FAQ)
Q:程序重启后错过的时间点会补跑吗?
A:不会。robfig/cron 是纯内存调度器,进程停了就丢了。关键任务需要持久化调度的话,考虑 Quartz 类的外部调度中心,或用系统 cron + 幂等脚本。
Q:AddFunc 的函数 panic 会怎样?
A:v3 默认 recover 并记日志(取决于 logger 配置),调度器本身不会挂。但建议函数内部自己兜住错误,避免任务状态不一致。
Q:能动态增删任务吗?
A:可以。AddFunc 返回 EntryID,之后 c.Remove(id) 删除;运行中随时可加新任务,c.Entries() 查看现有调度。