Cron 源码阅读
robfig/cron/v3
是一个 Golang 的定时任务库,支持 cron表达式。Cron 的源码真实教科书级别的存在,真的把低耦合高内聚体现地淋漓尽致,另外其中涉及的装饰器模式,并发处理等都很值得学习。
使用 cron 可以很方便的实现一个定时任务,如下:
go get github.com/robfig/cron/v3@v3.0.0
package main
import "github.com/robfig/cron/v3"
c := cron.New()
// 添加一个任务,每 30s 执行一次
c.AddFunc("30 * * * *", func() {
fmt.Println("Every hour on the half hour") })
// 开始执行(每个任务会在自己的 goroutine 中执行)
c.Start()
// 允许往正在执行的 cron 中添加任务
c.AddFunc("@daily", func() {
fmt.Println("Every day") })
// 检查上一个和下一个任务执行的时间
inspect(c.Entries())
..
c.Stop() // 停止调度,但正在运行的作业不会被停止
通过上面的示例,可以发现, cron 最常用的几个函数:
New()
: 实例化一个 cron 对象Cron.AddFunc()
: 向 Cron 对象中添加一个作业,接受两个参数,第一个是cron
表达式,第二个是一个无参无返回值的函数(作业)Cron.Stop()
: 停止调度,Stop 之后不会再有未执行的作业被唤醒,但已经开始执行的作业不会受影响。
关于 cron 表达式可以先看看 cron表达式的介绍与使用 这篇文章,一个 cron 表达式是一个由 5 个空格分隔的字符串,每一部分从左到右分别表示 秒,分, 时, 天,月, 星期,每个部分由数字和一些特殊字符表示一个约定的时间项,在 robfig/cron
中,每一部分允许的特殊字符如下:
这些特殊字符的含义如下:
*
: 匹配该字段所有值,如0 0 * 1 1 *
, 第三个字段为*
表示(1 月 1 日)每小时。/
: 表示范围增量,如*/12 * * * * *
表示每 12 秒执行一次,
: 用来分隔同一组中的项目,如* * 5,10,15 3,4 * *
表示每个三月或四月的 5, 10, 15 号(3.05, 3.10, 3.15, 4.05, 4.10,4.15)-
: 表示范围,如*/5 * 10-12 * * *
表示每天十点到十二点每五秒执行一次?
: 同*
cron 表达式虽然简单,但他却能满足定时任务复杂的使用场景,比如每周一到周五早上十点就可以表示为 0 0 10 * * 1-5
,除此之外,cron 还有几个预定义的时间表:
表示每隔多长时间时,你还可以使用预定义的 @every <duration>
如每隔十分钟就可以表示为 @every 10m
源码概览
cron 并不是一个很大的库,核心文件与作用如下:
chain.go
: 装饰器模式,使用 Chain 可以给一个作业添加多个装饰器,以实现日志记录等功能constantdelay.go
:顾名思义,提供了一个简单的常量延迟,如 每5分钟,最小粒度支持到秒cron.go
:提供核心功能logger.go
: 定义了一个 Logger 接口,使之能插入到结构化日志系统中option.go
:对默认行为的修改相关parser.go
:解析 cron 表达式spec.go
:
1.核心数据结构和接口
type Entry truct
Entry
是对添加到 Cron 中的作业的封装,每个 Entry 有一个 ID,除此之外,Entry 里保存了这个作业上次运行的时间和下次运行的时间。
type EntryID int
type Entry struct {
ID EntryID
Schedule Schedule
Next time.Time
Prev time.Time
WrappedJob Job
Job Job
}
type Cron struct
type Cron struct {
entries []*Entry // 保存了所有加入到 Cron 的作业
chain Chain
stop chan struct