文章目录
Cron
版本:v3.0.0
仓库:https://github.com/robfig/cron
cron是golang实现定时任务比较好的库, 这个库提供了一个简单而强大的接口,用于创建和管理基于cron表达式的定时任务。cron库的主要特点有:
- 基于cron表达式的任务调度
- 多任务支持
- 容错和错误处理
- 可靠性
- 易用的API
- 灵活性
- 并发安全
Cron快速使用
package main
import("fmt""github.com/robfig/cron/v3")funcmain(){
job := cron.New()
job.AddFunc("@every 1s",func(){
fmt.Println("hello world")})
job.Start()select{}}
上述简单的示例表示,每秒钟执行一次hello world打印。其中容易让人产生困惑的是
"@every 1s"
的含义,这是一条描述定时任务执行的
时间表达式
,下面将会具体介绍。
时间表达式
cron库支持用 6 个空格分隔的域来表示时间(在v3版本中新增了对秒级任务的支持):
# ┌────────────── second (0–59)
# │ ┌───────────── minute (0–59)
# │ │ ┌───────────── hour (0–23)
# │ │ │ ┌───────────── day of the month (1–31)
# │ │ │ │ ┌───────────── month (1–12)
# │ │ │ │ │ ┌───────────── day of the week (0–6) (Sunday to Saturday;
# │ │ │ │ │ │ 7 is also Sunday on some systems)
# │ │ │ │ │ │
# │ │ │ │ │ │
# * * * * * * <command to execute>
Field name | Mandatory? | Allowed values | Allowed special characters
---------- | ---------- | -------------- | --------------------------
Seconds | Yes | 0-59 | * / , -
Minutes | Yes | 0-59 | * / , -
Hours | Yes | 0-23 | * / , -
Day of month | Yes | 1-31 | * / , - ?
Month | Yes | 1-12 or JAN-DEC | * / , -
Day of week | Yes | 0-6 or SUN-SAT | * / , - ?
- 星号 (
*
):星号表示 cron 表达式将匹配该字段的所有值;例如,在第 5 个字段(月份)中使用星号将表示每个月。 - 斜杠 (
/
):斜杠用于描述范围的步长。例如,第2个字段(分钟)中的 3-59/15 表示该小时的第 3 分钟以及此后每 15 分钟一次。 - 逗号 (
,
):逗号用于分隔列表中的项目。例如,在第 6 个字段(星期几)中使用“MON,WED,FRI”将表示星期一、星期三和星期五。 - 连字符 (
-
):连字符用于定义范围。例如,在第 3 个字段(小时)9-17 表示上午 9 点到下午 5 点之间的每小时(含)。 - 问号(
?
):只能用在月和周的域中,用来代替*
,表示每月/周的任意一天。
最小分钟级任务
使用这种
以空格分隔的域来表示时间
的方式默认情况下只能支持到分钟级任务:
package main
import("fmt""github.com/robfig/cron/v3")funcmain(){
job := cron.New()
job.AddFunc("* * * * *",func(){
fmt.Println("hello world")})
job.Start()select{}}
表示每分钟执行一次
注意:实测在
v3.0.1
版本6个
*
是不会执行任务的。
查看表达是是否正确:
https://crontab.guru/
最小秒级任务
如果想要支持
秒级
任务,则需添加
cron.WithSeconds
选项:
package main
import("fmt""github.com/robfig/cron/v3")funcmain(){
job := cron.New()
job.AddFunc("* * * * * *",func(){
fmt.Println("hello world")})
job.Start()select{}}
表示每秒执行一次
注意:添加了
cron.WithSeconds
后6个
*
可以正确执行。
预定义的时间表
可以使用几个预定义计划之一来代替 cron 表达式。
Entry | Description | Equivalent To
----- | ----------- | -------------
@yearly (or @annually) | Run once a year, midnight, Jan. 1st | 0 0 0 1 1 *
@monthly | Run once a month, midnight, first of month | 0 0 0 1 * *
@weekly | Run once a week, midnight between Sat/Sun | 0 0 0 * * 0
@daily (or @midnight) | Run once a day, midnight | 0 0 0 * * *
@hourly | Run once an hour, beginning of hour | 0 0 * * * *
还可以安排作业以固定的时间间隔执行,从添加作业或运行 cron 时开始。这是通过格式化 cron 规范来支持的,如下所示:
@every <duration>
其中
“duration”
是
time.ParseDuration
接受的字符串(http://golang.org/pkg/time/#ParseDuration)
例子:
c := cron.New()
c.AddFunc("@hourly",func(){ fmt.Println("Every hour")})
c.AddFunc("@every 1h30m",func(){ fmt.Println("Every hour thirty")})
c.AddFunc("@every 1s",func(){ fmt.Println("Every seconds")})
c.Start()
cron还提供了
cron.Every
方法,用于直接接收
time.Duration
来设置任务间隔:
package main
import("fmt""time""github.com/robfig/cron/v3")funcmain(){
job := cron.New(cron.WithSeconds())
job.Schedule(cron.Every(1*time.Second), cron.FuncJob(func(){
fmt.Println("hello world")}))
job.Start()select{}}
时区
默认情况下任务是基于当前时区的,cron也可以设置不同的时区:
loLosAngeles,_:= time.LoadLocation("America/Los_Angeles")
job := cron.New(cron.WithLocation(loLosAngeles))
job.AddFunc("0 6 * * ?",func(){
fmt.Println("Every 6 o'clock at Los Angeles")})
或者:
job.AddFunc("CRON_TZ=Asia/Tokyo 0 6 * * ?",func(){
fmt.Println("Every 6 o'clock at Tokyo")})
Job
除了使用无参的回调方式,cron还提供了实现
job
接口的方式:
type Job interface{Run()}
实现
job
接口
package main
import("fmt""time""github.com/robfig/cron/v3")funcmain(){
job := cron.New(cron.WithSeconds())
j :=&myJob{}
job.AddJob("@every 1s", j)
job.Start()select{}}type myJob struct{
i int}func(j *myJob)Run(){
j.i++
fmt.Println("hello world:", j.i)}
执行结果:
hello world: 1
hello world: 2
hello world: 3
hello world: 4
hello world: 5
hello world: 6
选项
WithLocation
:指定时区WithParser
:使用自定义的解析器WithSeconds
:让时间格式支持秒WithLogger
:自定义日志WithChain
:Job 包装器
Job 包装器
Job 包装器可以在执行实际的Job前后添加一些逻辑
cron库提供了一些常用的包装器:
- cron.DelayIfStillRunning: 如果上周期的任务还在执行,则延迟此次并产生一条Info日志
- cron.SkipIfStillRunning:如果上周期的任务还在执行,则跳过此次并产生一条Info日志
- cron.Recover:捕获任务异常,并产生error日志
WithLogger
cron提供默认的标准输出日志打印
cron.DefaultLogger
和丢弃日志
cron.DiscardLogger
两种:
// DefaultLogger is used by Cron if none is specified.var DefaultLogger Logger =PrintfLogger(log.New(os.Stdout,"cron: ", log.LstdFlags))// DiscardLogger can be used by callers to discard all log messages.var DiscardLogger Logger =PrintfLogger(log.New(ioutil.Discard,"",0))
也可是使用
cron.PrintfLogger
和
cron.VerbosePrintfLogger
包装:
- cron.PrintfLogger: 只打印错误日志
- cron.VerbosePrintfLogger:打印详细日志
package main
import("fmt""github.com/robfig/cron/v3")funcmain(){
job := cron.New(
cron.WithLogger(cron.VerbosePrintfLogger(
log.New(os.Stdout,"cron: ", log.LstdFlags),)),)
job.AddFunc("@every 1s",func(){
fmt.Println("hello world")})
job.Start()select{}}
支持毫秒级任务
cron库给出的方法最小只能支持到秒级任务,如果想要精确到毫秒级任务,则需要重新实现
Schedule
接口
// Schedule describes a job's duty cycle.type Schedule interface{// Next returns the next activation time, later than the given time.// Next is invoked initially, and then each time the job is run.Next(time.Time) time.Time
}
自定义一个
ConstantDelaySchedule
结构体,并给出
Every
方法(这边我限制最小到200ms)
package cron
import"time"// ConstantDelaySchedule represents a simple recurring duty cycle, e.g. "Every 5 minutes".// It does not support jobs more frequent than once a second.type ConstantDelaySchedule struct{
Delay time.Duration
}// Every returns a crontab Schedule that activates once every duration.// Delays of less than a second are not supported (will round up to 1 second).// Any fields less than a Second are truncated.funcEvery(duration time.Duration) ConstantDelaySchedule {
min :=200* time.Millisecond
if duration < min {
duration = min
}return ConstantDelaySchedule{
Delay: duration,}}// Next returns the next time this should be run.// This rounds so that the next activation time will be on the second.func(schedule ConstantDelaySchedule)Next(t time.Time) time.Time {return t.Add(schedule.Delay)}
调用:
package main
import("fmt""time""github.com/robfig/cron/v3")funcmain(){
job := cron.New(cron.WithSeconds())
j :=&myJob{
t: time.Now(),}
job.Schedule(Every(200*time.Millisecond), j)
job.Start()select{}}type myJob struct{
i int
t time.Time
}func(j *myJob)Run(){
j.i++
now := time.Now()
sub := now.Sub(j.t)
j.t = now
fmt.Printf("hello world: %d, duraction : %d ms \n", j.i, sub.Milliseconds())}
结果:
hello world: 1, duraction :201 ms
hello world: 2, duraction :201 ms
hello world: 3, duraction :201 ms
hello world: 4, duraction :201 ms
hello world: 5, duraction :201 ms
hello world: 6, duraction :200 ms
hello world: 7, duraction :201 ms
hello world: 8, duraction :201 ms
hello world: 9, duraction :201 ms
版权归原作者 Daniel YK 所有, 如有侵权,请联系我们删除。