cron
https://github.com/robfig/cron 这个定时任务比较流行,我在项目中也在用。不过在阅读源码前,为了保证任务是串行运行,我还加了处理代码。
看了源码后才知道本身就是支持的。
源码学习
整理类图如下
说明
核心概念其实就3个。
第一个是怎么定义调度。
包括2种,一种是crontab那种,不过这里包括了秒。 秒 分 时 几号 几月 周几
第二种时文字描述型的,以 @
符号开头,如 @yearly @every 3ms
第二个就是怎么调度。
他是通过比特位来计算的。譬如 几月, 有效月是1~12月,那么 就是1~12的比特位置1, 其他位置0. 当你选3月时,只有3月的比特位是1.
如此就可以计算出下次运行的时间。
第三个是运行任务。Cron结构体干的事。
把待运行的entries 先计算好下一次的运行时间。
然后排序,时间按升序排,时间无效的排最后。
没有合适的任务,就启动一个超长的timer, 否则选离得最近的时间启个timer.
然后就是for循环处理事件:
-
到时间了。运行到时间的Job(启动goroutine运行的), 并计算好下一次运行时间。
-
有新添加任务。停止timer, 重新计算。
-
需要取快照。把entries 塞到channel 里。这时 Entries() 调用返回拿到数据。
-
stop, 停止timer, 结束goroutine.
-
删除, 停止timer, 删除entry, 重新计算。
使用
官网示例是:
c := cron.New()
c.AddFunc("30 * * * *", func() { fmt.Println("Every hour on the half hour") })
c.AddFunc("30 3-6,20-23 * * *", func() { fmt.Println(".. in the range 3-6am, 8-11pm") })
c.AddFunc("CRON_TZ=Asia/Tokyo 30 04 * * *", func() { fmt.Println("Runs at 04:30 Tokyo time every day") })
c.AddFunc("@hourly", func() { fmt.Println("Every hour, starting an hour from now") })
c.AddFunc("@every 1h30m", func() { fmt.Println("Every hour thirty, starting an hour thirty from now") })
c.Start()
我个人建议日志写文件,可以结合其他的logger 库。
然后把recover, 串行运行等加上。
logger := cron.DefaultLogger
c := cron.New(
cron.WithChain(cron.Recover(logger), cron.SkipIfStillRunning(logger)))
c.AddFunc("30 * * * *", func() { fmt.Println("Every hour on the half hour") })
c.AddFunc("30 3-6,20-23 * * *", func() { fmt.Println(".. in the range 3-6am, 8-11pm") })
c.AddFunc("CRON_TZ=Asia/Tokyo 30 04 * * *", func() { fmt.Println("Runs at 04:30 Tokyo time every day") })
c.AddFunc("@hourly", func() { fmt.Println("Every hour, starting an hour from now") })
c.AddFunc("@every 1h30m", func() { fmt.Println("Every hour thirty, starting an hour thirty from now") })
c.Start()// 如果是web服务等,用Start. 如果就是单纯的cron跑任务。用Run()
默认的版本是标准的cron 格式,时间粒度只能到分。如果要支持秒级的定时任务。需要自动一下。
logger := cron.DefaultLogger
c := cron.New(
cron.WithChain(cron.Recover(logger), cron.SkipIfStillRunning(logger)),
cron.WithParser(cron.NewParser(cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor)),
)
...
c.Start()// 如果是web服务等,用Start. 如果就是单纯的cron跑任务。用Run()