如何使用Golang开发定时任务程序_Golang定时器与调度项目实战

9次阅读

time.Ticker需显式Stop防goroutine泄漏;cron/v3注意时区与DelayIfStillRunning防重入;gocron仅限单机内存调度;定时任务必设超时context并监控goroutine数。

如何使用Golang开发定时任务程序_Golang定时器与调度项目实战

time.Ticker 做简单周期任务,但别让它泄漏 goroutine

如果你只需要每 5 秒查一次数据库、每分钟 ping 一次服务,time.Ticker 足够轻量。但它不会自动停止,一旦启动就一直跑,容易在服务重启或配置变更时积 goroutine。

  • 必须显式调用 ticker.Stop(),通常放在 defer 或上下文取消时
  • 避免在 for-select 循环里直接写业务逻辑,先检查 ctx.Done() 再处理 tick
  • 不要把 time.Tick()(底层无 Stop 接口)用于长期运行的服务
ticker := time.NewTicker(30 * time.Second) defer ticker.Stop() 

for { select { case <-ctx.Done(): return case <-ticker.C: doWork() } }

github.com/robfig/cron/v3 解析 crontab 表达式,注意时区和并发控制

标准库不支持类似 0 0 * * * 这种表达式,cron/v3 是事实标准,但默认使用本地时区,且每个任务单独起 goroutine —— 如果任务执行慢,可能堆积多个并发实例。

  • 初始化时传入 cron.WithLocation(time.UTC) 避免因服务器时区导致误触发
  • cron.WithChain(cron.Recover(cron.DefaultLogger), cron.DelayIfStillRunning(cron.DefaultLogger)) 防止重入
  • DelayIfStillRunningSkipIfStillRunning 更安全:前者排队,后者直接丢弃

gocron 做内存级多任务调度,但别把它当分布式方案

gocron 适合单机多定时任务管理,API 直观,支持链式注册、立即运行、手动暂停/恢复。但它所有状态都在内存里,进程一挂,所有未触发任务就丢了。

  • 任务函数必须是无状态的,或自行处理幂等(比如加数据库唯一约束)
  • 如果需要“至少执行一次”,得配合外部存储(如 redis + lua 记录 last_run_at)
  • 不要用它做跨机器协同 —— 它没有节点发现、选举或任务分片能力

遇到 context deadline exceeded 和任务卡死,先查底层阻塞点

定时任务看似简单,但实际常因 I/O 阻塞、锁竞争或 context 传递不完整导致整个调度器僵住。典型现象是日志停更、新任务不再触发,但进程还在。

立即学习go语言免费学习笔记(深入)”;

  • 所有 http 请求、数据库查询、文件读写,都必须带超时 context:ctx, cancel := context.WithTimeout(parentCtx, 5*time.Second)
  • 避免在定时任务里用 log.printf 替代结构化 logger,高频率打日志可能阻塞输出缓冲区
  • runtime.NumGoroutine() 定期采样,若持续上涨,大概率是某个任务没正确释放资源

真正难的不是怎么设间隔,而是让每次触发都可预期、可追踪、可中断。很多线上事故,都始于一个没设超时的 http.Get 调用,卡住了整个 cron.Entry 的 goroutine。

text=ZqhQzanResources