10log.Println( "Starting...")
11
12c := cron.New // 新建一个定时任务对象
13c.AddFunc( "* * * * * *", func{
14log.Println( "hello world")
15}) // 给对象增加定时任务
16c.Start
17select{
18}
19}
这个小功能用 time.Ticker也可以实现,我们为什么要使用cron来完成呢?别急接着往下面看就知道了。
cron表达式
我们在上面demo中使用了 AddFunc,第一个参数我们传递了一个字符串是: "* * * * * *",这六个 *是指什么呢?
1┌───────────── second范围 ( 0- 60)
2│ ┌───────────── min ( 0- 59)
3│ │ ┌────────────── hour( 0- 23)
4│ │ │ ┌─────────────── dayof month( 1- 31)
5│ │ │ │ ┌──────────────── month( 1- 12)
6│ │ │ │ │ ┌───────────────── dayof week ( 0- 6) ( 0to6are Sunday to
7│ │ │ │ │ │ Saturday)
8│ │ │ │ │ │
9│ │ │ │ │ │
10* * * * * *
匹配符号
example
比如我们的手机卡假设都是在每个月的开始时间就更新资费:
"0 0 0 1 * *"// 表示每个月1号的00:00:00
"0 1 1 1 * *"// 表示每个月1号的01:01:00
每隔5秒执行一次: "*/5 * * * * ?"
每隔1分钟执行一次: "0 */1 * * * ?"
每天23点执行一次: "0 0 23 * * ?"
每天凌晨1点执行一次: "0 0 1 * * ?"
每月1号凌晨1点执行一次: "0 0 1 1 * ?"
在26分、29分、33分执行一次: "0 26,29,33 * * * ?"
每天的0点、13点、18点、21点都执行一次: "0 0 0,13,18,21 * * ?"
pre-defined schedules
github.com/robfig/cron 包还给我门提供了一些预定义的模式:
| Entry | Deion | 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 * * * * |
| @every <duration> | every duration |
1c.AddFunc( "@every 1h30m", func{ fmt.Println( "Every hour thirty") })
golang cron主要的设计思路
主要类型或接口说明(借用大佬)
1typeCron struct{
2entries []*Entry
3stop chanstruct{} // 控制 Cron 实例暂停
4add chan*Entry // 当 Cron 已经运行了,增加新的 Entity 是通过 add 这个 channel 实现的
5snapshot chan[]*Entry // 获取当前所有 entity 的快照
6running bool// 当已经运行时为true;否则为false
7}
注意,Cron 结构没有导出任何成员。
注意:有一个成员 stop,类型是 struct{},即空结构体。
1type Entry struct {
2// The schedule on which this job should be run.
3// 负责调度当前 Entity 中的 Job 执行
4Schedule Schedule
5
6// The next time the job will run. This is the zero time if Cron has not been
7// started or this entry's schedule is unsatisfiable
8// Job 下一次执行的时间
9Next time.Time
10
11// The last time this job was run. This is the zero time if the job has never
12// been run.
13// 上一次执行时间
14Prev time.Time
15
16// The Job to run.
17// 要执行的 Job
18Job Job
19}
1type Job interface{
2Run
3}
由于 Entity中需要 Job类型,因此,我们希望定期运行的任务,就需要实现 Job接口。同时,由于 Job接口只有一个无参数无返回值的方法,为了使用方便,作者提供了一个类型:
type FuncJob func
它通过简单的实现 Run 方法来实现 Job 接口:
func (f FuncJob) Run { f }
这样,任何无参数无返回值的函数,通过强制类型转换为 FuncJob,就可以当作 Job来使用了, AddFunc方法 就是这么做的。
1typeSchedule interface {
2// Return the nextactivation time, later than the given time.
3// Next is invoked initially, andtheneach timethe job is run.
4// 返回同一 Entity 中的 Job 下一次执行的时间
5Next( time.Time) time.Time
6}
Schedule的具体实现通过解析 Cron表达式得到。
库中提供了 Schedule的两个具体实现,分别是 SpecSchedule和 ConstantDelaySchedule。
① SpecSchedule
1type SpecSchedule struct {
2Second, Minute, Hour, Dom, Month, Dow uint64
3}
从开始介绍的 Cron 表达式可以容易得知各个字段的意思,同时,对各种表达式的解析也会最终得到一个 SpecSchedule 的实例。库中的 Parse 返回的其实就是 SpecSchedule 的实例(当然也就实现了 Schedule 接口)。
② ConstantDelaySchedule
1type ConstantDelaySchedule struct{
2Delay time.Duration // 循环的时间间隔
3}
这是一个简单的循环调度器,如:每 5 分钟。注意,最小单位是秒,不能比秒还小,比如 毫秒。
通过 Every函数可以获取该类型的实例,如:
1constDelaySchedule := Every(5e9)
得到的是一个每 5 秒执行一次的调度器。
函数调用
1funcNew* Cron{
2return&Cron{
3entries: nil,
4add: make( chan*Entry),
5stop: make( chanstruct{}),
6snapshot: make( chan[]*Entry),
7running: false,
8}
9}
可见实例化时,成员使用的基本是默认值;
② 解析 Cron 表达式
1funcParse(spec string) (_ Schedule, err error)
spec 可以是:
- Full crontab specs, e.g. “* * * * * ?”
- Deors, e.g. “@midnight”, “@every 1h30m”
② 成员方法
1// 将 job 加入 Cron 中
2// 如上所述,该方法只是简单的通过 FuncJob 类型强制转换 cmd,然后调用 AddJob 方法
3func(c *Cron)AddFunc(spec string, cmd func) error
4
5// 将 job加入 Cron中
6// 通过 Parse函数解析 cron表达式 spec的到调度器实例 (Schedule),之后调用 c. Schedule方法
7func(c *Cron)AddJob(spec string, cmd Job) error
8
9// 获取当前 Cron总所有 Entities的快照
10func(c *Cron)Entries[]* Entry
11
12// 通过两个参数实例化一个 Entity,然后加入当前 Cron中
13// 注意:如果当前 Cron未运行,则直接将该 entity加入 Cron中;
14// 否则,通过 add这个成员 channel将 entity加入正在运行的 Cron中
15func(c *Cron)Schedule(schedule Schedule, cmd Job)
16
17// 新启动一个 goroutine运行当前 Cron
18func(c *Cron)Start
19
20// 通过给 stop成员发送一个 struct{}{} 来停止当前 Cron,同时将 running 置为 false
21// 从这里知道,stop 只是通知 Cron 停止,因此往 channel 发一个值即可,而不关心值是多少
22// 所以,成员 stop 定义为空 struct
23func(c *Cron)Stop
24
如果对每个方法调用还是不了解可以去看一下每隔函数的实现源码
example
1packagemain
2
3import(
4"log"
5
6"github.com/robfig/cron"
7)
8
9typeHello struct{
10Str string
11}
12
13func(h Hello)Run{
14log.Println(h.Str)
15}
16
17funcmain{
18log.Println( "Starting...")
19
20c := cron.New
21h := Hello{ "I Love You!"}
22// 添加定时任务
23c.AddJob( "*/2 * * * * * ", h)
24// 添加定时任务
25c.AddFunc( "*/5 * * * * * ", func{
26log.Println( "hello word")
27})
28
29s, err := cron.Parse( "*/3 * * * * *")
30iferr != nil{
31log.Println( "Parse error")
32}
33h2 := Hello{ "I Hate You!"}
34c.Schedule(s, h2)
35// 其中任务
36c.Start
37// 关闭任务
38deferc.Stop
39select{
40}
41}
参考
GO语言版CRONTAB
官方文档
参考
GO语言版CRONTAB
官方文档