文章目录
- 写在前面
- 内容
- 基础库
- for + sleep
- Ticker
- 第三方库
- cron
- 安装
- @表达式
- linux cron 表达式
- job
- schedule
- 参考
写在前面
记录 Go 的一些关于定时任务的处理
内容
基础库
for + sleep
func main() {for {time.Sleep(5 * time.Second)fmt.Println("every 5s")}
}
Ticker
func main() {ticker := time.NewTicker(5 * time.Second)for range ticker.C {fmt.Println("every 5s")}
}
第三方库
cron
前面两个都是基础库里的东西,可以实现一些基本的定时功能,但对于一些稍显复杂的定时任务需求,就需要我们自己去做更多的内容来支持,比如说需求需要在每个月的 1 号,或是每周一之类的来进行任务,就需要我们自己去写代码来判断了。而 cron 这个第三方库,则能很好地支撑我们这类的需求。
安装
go get github.com/robfig/cron/v3@v3.0.0
cron 对于定时的设置有几种写法,都能达到同样的目的。
@表达式
func descriptors() {c := cron.New()// @every// 支持秒级别,会在启动 6s 后执行_, errEveryS := c.AddFunc("@every 6s", func() {fmt.Println("every 6s")})if errEveryS != nil {fmt.Printf("errEveryS: %v\n", errEveryS)}// 0.1 min = 6s_, errEveryM := c.AddFunc("@every 0.1m", func() {fmt.Println("every 0.1m")})if errEveryM != nil {fmt.Printf("errEveryM: %v\n", errEveryM)}// @yearly @annually 每年执行一次,即 1 月 1 日的时候开始执行_, errYearly := c.AddFunc("@yearly", func() {fmt.Println("yearly")})if errYearly != nil {fmt.Printf("errYearly: %v\n", errYearly)}_, errAnnually := c.AddFunc("@annually", func() {fmt.Println("annually")})if errAnnually != nil {fmt.Printf("errAnnually: %v\n", errAnnually)}// @monthly 每个月的第一天的零点执行_, errMonthly := c.AddFunc("@monthly", func() {fmt.Println("monthly")})if errMonthly != nil {fmt.Printf("errMonthly: %v\n", errMonthly)}// @weekly 每周的第一天零点执行,这个第一天可能是周六,也可能是周日_, errWeekly := c.AddFunc("@weekly", func() {fmt.Println("weekly")})if errWeekly != nil {fmt.Printf("errWeekly: %v\n", errWeekly)}// @hourly 每小时执行一次,启动后一小时执行_, errHourly := c.AddFunc("@hourly", func() {fmt.Println("hourly")})if errHourly != nil {fmt.Printf("errHourly: %v\n", errHourly)}// @daily @midnight,每天执行一次,在每天的零点执行_, errDaily := c.AddFunc("@daily", func() {fmt.Println("daily")})if errDaily != nil {fmt.Printf("errDaily: %v\n", errDaily)}_, errMidnight := c.AddFunc("@midnight", func() {fmt.Println("midnight")})if errMidnight != nil {fmt.Printf("errMidnight: %v\n", errMidnight)}c.Start()defer c.Stop()select {}
}
linux cron 表达式
如果我们想要更灵活的一些处理,就需要学习下 Linux 的 cron 表达式,参照如下:
* * * * *- - - - -| | | | || | | | +----- day of week (0 - 7) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat| | | +---------- month (1 - 12) OR jan,feb,mar,apr ...| | +--------------- day of month (1 - 31)| +-------------------- hour (0 - 23)+------------------------- minute (0 - 59)
只要我们学会了 Linux 的 cron 表达式的使用,也就可以直接用在 cron 库上,我们可以看到 Linux cron 表达式的标准实现里,最小的时间单位是支持到分钟级别,秒级别的话我们可以用上面的方案处理。
func linux() {c := cron.New()// 每1分钟执行,等同于 */1 * * * * 和 @every 1m_, err := c.AddFunc("* * * * *", func() {fmt.Println("* * * * *")})if err != nil {fmt.Printf("err: %v\n", err)}// 每 5 分钟执行,等同于 @every 5m// / 表示频率_, err1 := c.AddFunc("*/5 * * * *", func() {fmt.Println("* * * * *")})if err1 != nil {fmt.Printf("err1: %v\n", err1)}// 每个小时的 01 分执行_, err2 := c.AddFunc("1 * * * *", func() {fmt.Println("1 * * * *")})if err2 != nil {fmt.Printf("err2: %v\n", err2)}// 每个月的第一天的 0 点 0 分运行,相当于 @monthly_, err3 := c.AddFunc("0 0 1 * *", func() {fmt.Println("0 0 1 * *")})if err3 != nil {fmt.Printf("err3: %v\n", err3)}// 每周的第一天(周六或周日)的 0 点 0 分运行,相当于 @weekly// 像一些发奖需求,在每周一进行发奖,就可以写成 0 0 * * 1_, err4 := c.AddFunc("0 0 * * 0", func() {fmt.Println("0 0 * * 0")})if err4 != nil {fmt.Printf("err4: %v\n", err4)}// 每天的 0 点 0 分运行一次,相当于 @daily 和 @midnight_, err5 := c.AddFunc("0 0 * * *", func() {fmt.Println("0 0 * * *")})if err5 != nil {fmt.Printf("err5: %v\n", err5)}// 每年运行一次,在 1 月 1 日 0 点 0 分,相当于 @yearly @annually_, err6 := c.AddFunc("0 0 1 1 *", func() {fmt.Println("0 0 1 1 *")})if err6 != nil {fmt.Printf("err6: %v\n", err6)}// 每小时运行一次,相当于 @hourly_, err7 := c.AddFunc("0 * * * *", func() {fmt.Println("0 * * * *")})if err7 != nil {fmt.Printf("err7: %v\n", err7)}// - 表示范围 , 表示列表// 这里的意思是,在每天的 3 点到 6 点和 20 点到 23 点这两个范围内,每个 30 分执行一次// 也就是 3 点 30,4 点 30,5 点 30 这样_, err8 := c.AddFunc("30 3-6,20-23 * * *", func() {fmt.Println("30 3-6,20-23 * * *")})if err8 != nil {fmt.Printf("err8: %v\n", err8)}c.Start()defer c.Stop()select {}
}
更进一步的,假如我们在做海外的应用,有时需要根据时区来进行定时任务,那么可以这么写:
func timezone() {c := cron.New()// 在东京时区,每天的 4 点 30 分运行_, err := c.AddFunc("CRON_TZ=Asia/Tokyo 30 04 * * *", func() {fmt.Println("CRON_TZ=Asia/Tokyo 30 04 * * *")})if err != nil {fmt.Printf("err: %v\n", err)}c.Start()defer c.Stop()// 或者在 cron 初始化的时候指定时区tc, _ := time.LoadLocation("Asia/Tokyo")c1 := cron.New(cron.WithLocation(tc))_, err1 := c1.AddFunc("30 04 * * *", func() {fmt.Println("30 04 * * *")})if err1 != nil {fmt.Printf("err1: %v\n", err1)}c1.Start()defer c1.Stop()select {}
}
job
在工作中,为了做到封装的效果,我们可以实现 Job 接口,把任务给封装起来:
func job() {c := cron.New()j1 := MyJob{name: "1"}_, errJob1 := c.AddJob("@every 5s", &j1)if errJob1 != nil {fmt.Printf("errJob1: %v", errJob1)}j2 := MyJob{name: "2"}_, errJob2 := c.AddJob("@every 6s", &j2)if errJob2 != nil {fmt.Printf("errJob2: %v", errJob2)}c.Start()select {}
}// MyJob 对定时任务进行包装
type MyJob struct {name string
}// Run 实现 Job 接口里的 Run 方法
func (j *MyJob) Run() {fmt.Printf("myjob : %s\n", j.name)
}
schedule
如果我们想动态的调整任务的下一次运行的时间,就可以通过实现 Schedule 接口来处理:
func schedule() {c := cron.New()// 为了方便,这里用 FuncJob,就不去创建 Job 结构体了 c.Schedule(&MySchedule{}, cron.FuncJob(func() {fmt.Println("hello")}))c.Start()select {}}type MySchedule struct {
}// Next 实现 Schedule 接口的 Next 方法
// Start 的时候,会触发 Next 方法,我们可以在这个的基础上,返回下一次的运行时间
// 之后每次触发都会调用 Next,我们就可以在这里做文章,动态修改下一次的触发时间
func (s *MySchedule) Next(t time.Time) time.Time {fmt.Printf("now time: %v\n", t)result := t.Add(5 * time.Second)return result
}
参考
crontab执行时间计算
cron github