# go 的定时任务包 cron
robfig/cron
是 Go 语言实现的开源定时任务调度框架,核心代码是巧妙的使用 chan + select + for
实现了一个轻量级调度协程,不但语法简洁,而且具有很好的性能。
Cron 是 Go 中用于设置定时任务的一个库,需要注意的是,Cron 库分两个大版本,v1.2
和 v3.0
,其功能和 go get
地址都是不同的,注意区分。
v1.2
官方文档:https://pkg.go.dev/github.com/robfig/cronv3
官方文档:https://pkg.go.dev/github.com/robfig/cron/v3
cron github 仓库:https://github.com/robfig/cron
安装
安装如下所示
# v1.2
go get github.com/robfig/cron
# v3
go get github.com/robfig/cron/v3@v3.0.0
简单案例
package main
import (
"github.com/robfig/cron/v3"
"log"
"time"
)
func main() {
// 创建一个cron的实例
// 由于 V3 版本默认是不带秒数的,如果粒度要到秒请在创建 Cron 实例时加上 cron.WithSeconds()
c := cron.New(cron.WithSeconds())
//以实例创建一个定时任务
entryID, err := c.AddFunc("*/5 * * * * *", func() {
log.Println("每隔五秒执行一次")
})
if err != nil {
log.Println(err)
}
//控制启动与关闭
c.Start()
totalTime := 0
defer c.Stop()
for {
time.Sleep(time.Second)
totalTime += 1
if totalTime == 20 {
c.Remove(entryID)
log.Println("删除任务:", entryID)
}
}
}
输出信息:
2023/12/16 14:30:15 每隔五秒执行一次
2023/12/16 14:30:20 每隔五秒执行一次
2023/12/16 14:30:25 每隔五秒执行一次
Cron 表达式
cron 表达式是一个好东西,这个东西 Java、Go 语言、Linux 的 crontab 规则都一样。
但是 Go 语言和 Java 中都是可以精确到秒的,但是 Linux 中不行。所以在 go 和 java 中,可以有 5 位或者 6 位来表示定时任务,linux 只能是 5 位表达式。
# 6位包含秒
# 秒 分 时 日 月 星期
* * * * * *
# 5位包不含秒
# 分 时 日 月 星期
* * * * *
cron 表达式每个字段可以拥有值,只要当前时间和表达式匹配上了,就会触发条件:
字段名 | 是否必须 | 允许的值 | 允许的特定字符 |
---|---|---|---|
秒 | 是 | 0-59 | * / , - |
分 | 是 | 0-59 | * / , - |
时 | 是 | 0-23 | * / , - |
日 | 是 | 1-31 | * / , - ? |
月 | 是 | 1-12 或 JAN-DEC | * / , - |
星期 | 否 | 0-6 或 SUM-SAT | * / , - ? |
特定字符说明
符号
*
表示 cron 表达式能匹配该字段的所有值。
# 因为每秒时间值都匹配上,所以每秒都执行
* * * * * *
# 因为在秒这个字段上,只有0才匹配上,所以是每分钟的0秒执行
0 * * * * *
# 因为在秒和分钟这2个字段上,只有0才匹配上,所以每小时的0分0秒执行
0 0 * * * *
# 每天的0时0分0秒执行
0 0 0 * * *
符号
/
表示增长间隔
这个符号,其实就为了简化写法用的,如果没有这个的话,就需要用 ,
列出具体值,表达式会比较长。注意:这个不是除法操作!
# 因为秒的初始值是0,每次增加10,其实就是0,10,20,30,40,50
*/10 * * * * *
# 示每分钟的第 3 秒开始执行一次,之后每隔 10 秒执行一次(即 3、13、23、33,43,53 这些时间点执行),这里也可以表示为:3/10
3-59/10 * * * * *
符号
,
表示枚举值
# 即 3、13、23、33,43,53 这些时间点执行
3,13,23,33,43,53 * * * * *
符号
-
表示范围
表示一个范围,如小时字段的值为 9-17
表示 9 am 到 5 pm 直接每个小时(包括 9 和 17)
# 9 am 到 5 pm 直接每个小时(包括 9 和 17)
* * 9-17 * * *
符号
?
只用于日和星期,表示不指定值,可以用于代替*
和 *
等效吧
* * * * * ?
预定义的时间格式
您可以使用几个预定义的表达式来代替上表的表达式,使用如下:
输入 | 描述 | 等式 |
---|---|---|
@yearly (or @annually) |
每年一次,1 月 1 日午夜 | 0 0 0 1 1 * |
@monthly |
每月运行一次,每月第一天午夜 | 0 0 0 1 * * |
@weekly |
每周运行一次,周六/周日之间的午夜 | 0 0 0 * * 0 |
@daily (or @midnight) |
每天午夜运行一次 | 0 0 0 * * * |
@hourly |
每小时运行一次 | 0 0 * * * * |
还可以安排作业以固定的间隔执行,从添加作业或运行 cron 时开始。这是通过如下格式化 cron 规范来支持的:
@every <duration>
// 例如
c := cron.New()
c.AddFunc("@every 1h30m", func() {
fmt.Println("Every hour thirty, starting an hour thirty from now")
})
写完表达式后,如果想要校验是否正确可以用这个工具:http://cron.ciding.cc/
时区
默认情况下,所有时间都是基于当前时区的,也可自定义:在时间字符串前面添加一个 CRON_TZ= +具体时区
一些常用的时区:
- 东京时区:Asia/Tokyo
- 纽约时区:America/New_York
- 上海时区:Asia/Shanghai
- 香港时区:Asia/Hong_Kong
创建 cron 对象时增加一个时区选项 cron.WithLocation(location)
,location 为 time.LoadLocation(zone)
加载的时区
对象,zone 为具体的时区格式。
package main
import (
"fmt"
"github.com/robfig/cron/v3"
"time"
)
func main() {
//直接配置时区,纽约时区
nyc, _ := time.LoadLocation("America/New_York")
// cron.New(cron.WithLocation(time.UTC))
c := cron.New(cron.WithLocation(nyc),cron.WithSeconds())
c.AddFunc("*/5 * * * * ?", func() {
fmt.Println("Every 5 second at New York")
})
// 参数里面配置时区
c.AddFunc("CRON_TZ=Asia/Tokyo */5 * * * * ?", func() {
fmt.Println("Every 5 second at Tokyo")
})
c.Start()
select {}
}
自定义定时任务以及多个定时任务
自定义定时任务只需要实现 Job 接口的 Run 方法。
package main
import (
"fmt"
"github.com/robfig/cron/v3"
"time"
)
type Task1 struct {
Name string
}
// 自定义定时任务只需要实现Job接口的Run方法
func (t *Task1) Run() {
fmt.Println("Task1: ", t.Name)
}
type Task2 struct {
Name string
}
// 自定义定时任务只需要实现Job接口的Run方法
func (t *Task2) Run() {
fmt.Println("Task2: ", t.Name)
}
func main() {
cronTab := cron.New(cron.WithSeconds())
// 定义定时器调用的任务函数
// 定时任务
// cron表达式,每五秒一次
spec := "*/5 * * * * ?"
//定义定时器调用的任务函数
task := func() {
fmt.Println("hello world", time.Now())
}
// 添加多个定时器
cronTab.AddFunc(spec, task)
cronTab.AddJob(spec, &Task1{Name: "tom"})
cronTab.AddJob(spec, &Task2{Name: "merry"})
// 启动定时器
cronTab.Start()
// 关闭,但是不能关闭已经在执行中的任务
defer cronTab.Stop()
// 阻塞主线程停止
select {}
}