golang cron 定时任务
cron 用法
首先先把第三方库下载下来:
$ go get -v -u github.com/robfig/cron
我们来看一个小的demo:每隔一秒打印"hello world"
这个小功能同样可以用time模块中的Ticker类来实现。 cron的工作原理是什么呢?也许你很好奇吧?
cron表达式
在上文中我们应用了AddFunc作为核心组件,在第一个参数中传入了一个字符串代表:'* * * * **'这些星号的作用是什么?
┌─────────────second 范围为(分钟, 从0到60分)
│ ┌───────────── min 数量级设定在(分钟, 具体数值介于)
│ ↓ ↓
├── minute: from "zero" 到 "fifty nine"
├── hour: from "zero" 到 "twenty three"
├── day: from "one" 到 "thirty one"
├── month: from "one" 到 "twelve"
└── weekday: from "zero" 到 "six", 其中"zero"代表Sunday,"one"代表Monday,...,"six"代表Saturday
匹配符号
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 | 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 * * * * |
| @every |
every duration |
c.AddFunc("@every 1h30m", func() { fmt.Println("Every hour thirty") })
golang cron主要的设计思路
主要类型或接口说明 (借用大佬)
类型 Cron 的结构定义如下:它包含一个动态分配的空间用于存储 Entry 类型的对象切片;一个停止通道用于控制 Cron 实例的暂停状态;一个通道用于在已运行状态下增加新的 Entry 对象;一个通道用于获取当前所有 Entry 对象的快照;以及一个布尔标志来指示当前是否处于运行状态。
注意,Cron 结构没有导出任何成员。
注意:有一个成员 stop,类型是 struct{},即空结构体。
type Entry struct {
// The schedule on which this job should be run.
// 负责调度当前 Entity 中的 Job 执行
Schedule Schedule
当该作业下次运行时, 这将是如果Cron未启动或此条目调度不可行的零时间. 该作业下次运行的时间为Next time.Time
// 当这个任务上一次运行时. 这表示零次运行的情况.
// 最后一次执行时间
Prev time.Time
// The Job to run.
// 要执行的 Job
Job Job
}
type Job interface {
Run()
}
因为 Entity 需要将任务分配给 Job 类型, 希望能够定期执行的任务必须实现该接口。同时该接口仅包含一个无需参数和返回值的方法, 为了操作简便, 作者为此提供了相应的类:
type FuncJob func()
它通过简单的实现 Run() 方法来实现 Job 接口:
func (f FuncJob) Run() { f() }
所有没有参数且不返回值的函数都被强制转换为 FuncJob后就可以被视为一个Job来使用;AddFunc方法正是按照这种方式进行操作。
type Schedule interface {
// Compute the next activation time, which must be after the provided time.
// The method is called initially and every subsequent execution of the job.
// It calculates the next activation time for a Job within the same Entity after it has been activated.
Next(time.Time) -> time.Time
}
Schedule 的具体实现通过解析 Cron 表达式得到。
该库实现了 Schedule 的具体两种实现方式,并包含SpecSchedule类和ConstantDelaySchedule类这两个核心组件。
① SpecSchedule
type SpecSchedule struct {
Second, Minute, Hour, Dom, Month, Dow uint64
}
从对Cron表达式的介绍中可以看出每个字段的具体含义。另外,在解析各种表达式时也会最终生成一个SpecSchedule实例。当使用Parse方法时返回的结果正是一个SpecSchedule实例(它同时也实现了 Schedule 接口)。
② ConstantDelaySchedule
type ConstantDelaySchedule struct {
Delay Duration // 其中Delay表示该结构体用于表示循环的时间间隔
}
该系统采用一种直观的循环调度机制,例如,在配置中设置为每隔5分钟执行一次任务.特别强调,最低可配置的时间间隔为1秒钟,不可细分为比1秒钟更短的时间片.
通过 Every 函数可以获取该类型的实例,如:
constDelaySchedule := Every(5e9)
得到的是一个每 5 秒执行一次的调度器。
函数调用
函数 New 返回一种 Cron 类型的对象 {
初始化了一个 Cron 对象,
其中 entries 为空,
add 设置为一个新的 *Entry 通道,
stop 开启了一个空结构体的通道,
snapshot 初始化为空,
并将 running 设定为初始值 false。
}
可见实例化时,成员使用的基本是默认值;
② 解析 Cron 表达式
func Parse(spec string) (_ Schedule, err error)
spec 可以是:
② 成员方法
// 将 job 作为任务注册到 Cron 中
// 如前所述,该方法仅通过将 FuncJob 类型强制转换 cmd 进而执行相应的操作
func (c *Cron) AddFunc(spec string, cmd func()) error
// 将 job 注册到 Cron 库中
// 利用 Parse 函数解析 cron 表达式 spec 以获取调度器实例
随后调用该实例的 Schedule 方法
func (c *Cron) AddJob(spec string, cmd Job) error {
// 注释:spec 是 cron 表达式字符串;cmd 是要执行的任务
return c.Schedule(Parse(spec))
}
// 获取当前 Cron 总所有 Entities 的快照
func (c *Cron) Entries() []*Entry
// 请注意,在当前Cron未运行的情况下,该Entity会被立即注入其中;而在Cron正在运行的情况下,则会通过指定的成员通道将其注入到运行中的Cron中
func (c *Cron) Schedule(schedule Schedule, cmd Job)
// 新启动一个 goroutine 运行当前 Cron
func (c *Cron) Start()
// 为了在处理Cron时停止它,并将其设置为不可运行。
// 为此,在处理Cron时只需通知其停止即可。
// 因此成员 stop 被定义为空结构体。
func (c *Cron) Stop()
如果对每个方法调用还是不了解可以去看一下每隔函数的实现源码
example:
