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

官方文档