Go 后端定时任务:从 time.Ticker 到分布式防重入

举报
yd_278073926 发表于 2026/11/12 06:11:26 2026/11/12
【摘要】 Go 后端定时任务:从 time.Ticker 到分布式防重入点餐小程序上线后,有一类需求迟早会冒出来:每天凌晨清理过期未支付订单、每小时汇总一次销量、每周一早上给老板推一份经营简报。这些活都不难写,难的是让它在生产环境里稳定地跑、不重跑、不漏跑。定时任务的坑,大多不在业务逻辑,而在调度和执行方式上。 time.Ticker:够用,但有三个坑最简单的写法是 time.NewTicker 加...

Go 后端定时任务:从 time.Ticker 到分布式防重入

点餐小程序上线后,有一类需求迟早会冒出来:每天凌晨清理过期未支付订单、每小时汇总一次销量、每周一早上给老板推一份经营简报。这些活都不难写,难的是让它在生产环境里稳定地跑、不重跑、不漏跑。定时任务的坑,大多不在业务逻辑,而在调度和执行方式上。

time.Ticker:够用,但有三个坑

最简单的写法是 time.NewTicker 加一个 for 循环:

ticker := time.NewTicker(time.Hour)
defer ticker.Stop()
for range ticker.C {
    syncOrders()
}

第一个坑是丢 tick:Ticker 的 channel 缓冲只有 1,如果某次任务执行耗时超过了间隔,下一个 tick 会被直接丢掉——这对“每小时汇总”可能是可接受的,但对“每天只跑一次的对账”就可能漏掉一次执行。第二个坑是漂移:如果用 time.Sleep 循环写,任务本身的耗时会累进到下一次执行时间里,跑着跑着就从凌晨 3 点漂到早上 6 点。第三个坑是泄漏:Ticker 用完必须 Stop(),否则它会一直持有 goroutine 和 channel,服务跑久了就是一处隐性泄漏。

cron 表达式:可读性更好,时区是重点

任务一多,Ticker 循环就难维护了,这时适合用 cron 表达式描述调度,例如 0 0 3 * * * 表示每天凌晨 3 点执行。比起手写时间算术,表达式一眼就能看懂,也方便改。

但 cron 有两个容易踩的点。一是位数:有的库用 5 位(分、时、日、月、周),有的用 6 位(多一位秒),照抄网上的表达式前先确认库的格式,错一位就是完全不同的执行时间。二是时区:cron 默认按进程所在机器的本地时区跑,容器默认是 UTC,写着“凌晨 3 点”的任务,实际会在北京时间 11 点执行。部署在云服务器上的 Go 服务,一定要在代码里把时区显式指定为 Asia/Shanghai,不要依赖机器默认设置。

多实例部署:防重入是必答题

单机跑定时任务,上面这些就够了。一旦服务扩到两个实例,问题就来了:两个实例会在同一时刻各自跑一遍清理任务,同一批订单可能被重复处理,同一份简报可能被重复推送。

最常见的解法是用分布式锁:任务开始前先抢锁,抢到了才执行,抢不到就直接跳过。Redis 的 SET key value NX EX 30 一行就能实现:NX 保证只有一个实例能拿到锁,EX 给锁一个 30 秒过期时间,防止拿到锁的实例崩溃后锁永远不释放。但要注意两点:锁的过期时间要大于任务的正常耗时,否则任务还没跑完锁就过期了,另一个实例会进来重跑;任务本身要尽量幂等——即使真的重跑一次,也不能造成重复扣款、重复发券这种不可逆的后果。

如果业务量还小,不想引入 Redis,也有更轻的办法:把定时任务独立成一个单独部署的实例,或者用数据库的一行“任务心跳”记录做互斥,抢到行锁的实例才执行。方案可以简单,但“同一时刻只跑一份”这个保证不能省。

失败了怎么办:重试要有度,沉默是最坏的

定时任务失败后最忌讳的是静默吞掉:日志里什么都没有,老板早上发现简报没来,才知道昨晚的任务挂了。正确的做法是每次执行都记录开始、结束、耗时和影响行数,失败时记 error 级日志并接告警。重试可以加,但要加退避间隔——失败后立刻原地重试三次,遇到的是下游服务抖动还好,遇到的是数据库挂了,只会把雪崩推得更快。

小结:一份上线前的检查清单

写定时任务之前,先过一遍这张清单:执行间隔会不会漂移、时区写死没有、Ticker 记得 Stop 了吗、多实例下谁来保证只跑一份、失败了有没有日志和告警、任务本身是不是幂等的。单机场景下 systemd 的 timer 也可以承担定时调度,简单可靠;但一旦涉及多实例、复杂调度,代码里的 cron 加分布式锁才是更通用的解法。定时任务的代码量通常不大,但它是凌晨三点替你值班的同事——上线前多花十分钟检查,半夜就少一次被叫醒的电话。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。