在项目的日常运行中,常常有需要用到定时任务的场景,比如考试即将截止的提醒,数据库的定时备份等。定时任务我们可以写脚本利用Linux的crontab命令来实现,在Go语言中也可以使用Cron定时任务库,本文将介绍此库的用法,并附上示例代码。
Cron是Go中用于设置定时任务的一个库,需要注意的是,Cron库分两个大版本,v1.2
和v3.0
,其功能和go get
地址都是不同的,注意区分。
安装:
go get github.com/robfig/cron
Cron v1.2
官方文档:https://pkg.go.dev/github.com/robfig/cron
使用示例:
package main
import (
"github.com/robfig/cron"
"log"
)
func testCornv1_2() {
log.Println("Starting...")
// 新建一个定时任务对象
c := cron.New()
// 给对象增加定时任务
c.AddFunc("*/5 * * * * *", func() {
log.Println("hello world1")
})
c.AddFunc("*/8 * * * * *", func() {
log.Println("hello world2")
})
// 启动定时任务
c.Start()
defer c.Stop()
}
func main() {
go testCornv1_2()
select {
//查询语句,保持程序运行
}
}
Cron v1.2
版本默认支持精确到秒的cron表达式,Linux系统原生支持的标准cron表达式只能精确到分钟。
在此推荐一个在线cron表达式解析器:https://crontab.guru/
┌─────────────second 范围 (0 - 60)
│ ┌───────────── min 范围 (0 - 59)
│ │ ┌────────────── hour 范围 (0 - 23)
│ │ │ ┌─────────────── day of month 范围 (1 - 31)
│ │ │ │ ┌──────────────── month 范围 (1 - 12)
│ │ │ │ │ ┌───────────────── day of week 范围 (0 - 6) (0 to 6 are Sunday to
│ │ │ │ │ │ Saturday)
│ │ │ │ │ │
│ │ │ │ │ │
* * * * * *
匹配符号
星号(*) :表示 cron 表达式能匹配该字段的所有值。如在第5个字段使用星号(month),表示每个月
斜线(/):表示增长间隔,如第2个字段(minutes) 值是 3-59/15,表示每小时的第3分钟开始执行一次,之后 每隔 15 分钟执行一次(即 3(3+0*15)、18(3+1*15)、33(3+2*15)、48(3+3*15) 这些时间点执行),这里也可以表示为:3/15
逗号(,):用于枚举值,如第6个字段值是 MON,WED,FRI,表示 星期一、三、五 执行
连字号(-):表示一个范围,如第3个字段的值为 9-17 表示 9am 到 5pm 直接每个小时(包括9和17)
问号(?):只用于 日(Day of month) 和 星期(Day of week),表示不指定值,可以用于代替 *
// 每5秒执行一次:*/5 * * * * *
// 每个工作日早上9点45分执行一次:0 45 9 * * 1,2,3,4,5
安装:
go get github.com/robfig/cron/v3@v3.0.0
Cron v3
官方文档:https://pkg.go.dev/github.com/robfig/cron/v3
Cron v3
相比v1.2
最大的变化就是支持定时任务的撤销功能,cron.Remove(cron.EntryID)
。cron.EntryID
在添加定时任务会返回。
示例代码:
cron_v3.go
package main
import (
"github.com/robfig/cron/v3"
"learn-go/corn/DB"
"log"
"time"
)
var c *cron.Cron
// InitCron 初始化并启动Cron
func InitCron() {
// 初始化定时任务对象
// Cron v3默认支持精确到分钟的cron表达式
// cron.WithSeconds()表示指定支持精确到秒的表达式
c = cron.New(cron.WithSeconds())
// 从数据库中获取已计划的任务并运行
for examId := range DB.DB {
id, err := addCron(examId, DB.DB[examId].Spec)
if err != nil {
log.Printf("InitCron err: %v", err)
}
// 更新任务id
DB.Update(examId, DB.DB[examId].Spec, id)
}
}
// StartCron 启动Cron
func StartCron() {
c.Start()
defer c.Stop()
select {}
}
// AddCron 运行一个定时任务
func AddCron(examId, spec string) int {
id, err := addCron(examId, spec)
if err != nil {
log.Println(err)
return 0
}
// 将定时任务添加到数据库
DB.Insert(examId, spec, id)
return id
}
func addCron(examId, spec string) (int, error) {
id, err := c.AddFunc(spec, func() {
log.Printf("推送考试提醒消息 examId: %v\n", examId)
})
if err != nil {
log.Printf("err: %v\n", err)
}
return int(id), err
}
// DeleteCron 删除定时任务
func DeleteCron(examId string) {
deleteCron(examId)
// 从数据库删除该定时任务数据
DB.Delete(examId)
}
func deleteCron(examId string) {
// 查询数据库获取cron id
ccron := DB.Query(examId)
id := ccron.Id
// 移除定时任务
c.Remove(cron.EntryID(id))
}
// UpdateCron 修改定时任务的执行时间
func UpdateCron(examId, spec string) {
id := updateCron(examId, spec)
// 更新数据库cron id
DB.Update(examId, spec, id)
}
func updateCron(examId, spec string) int {
// 删除原定时任务
DeleteCron(examId)
// 添加新的定时任务
id := AddCron(examId, spec)
return id
}
func main() {
InitCron()
go StartCron()
AddCron("6652", "*/1 * * * * *")
log.Printf("examId :%v Cron: %v \n", "6652", DB.Query("6652"))
AddCron("6616", "*/1 * * * * *")
log.Printf("examId :%v Cron: %v \n", "6616", DB.Query("6616"))
AddCron("6636", "*/3 * * * * *")
log.Printf("examId :%v Cron: %v \n", "6636", DB.Query("6636"))
// 获取id为1的定时任务的下一次一次运行时间
log.Println(c.Entry(1).Next.Unix())
time.Sleep(time.Second * 8)
// 获取id为1的定时任务的上一次运行时间
log.Println(c.Entry(1).Prev.Unix())
log.Println("-------------UpdateCron-------------")
UpdateCron("6652", "*/2 * * * * *")
time.Sleep(time.Second * 20)
log.Println("-------------DeleteCron-------------")
log.Printf("DB Before DeleteCron: %v\n", DB.DB)
DeleteCron("6616")
log.Printf("DB After DeleteCron: %v\n", DB.DB)
select {}
}
db.go
用map模拟数据库
package DB
// DB 模拟数据库
var DB map[string]Cron
type Cron struct {
Id int
Spec string
}
// 初始化数据库
func init() {
DB = map[string]Cron{
"8868": {
Id: 8,
Spec: "*/8 * * * * *",
},
}
}
func Insert(examId, spec string, id int) {
DB[examId] = Cron{
Id: id,
Spec: spec,
}
}
func Delete(examId string) {
delete(DB, examId)
}
func Update(examId, spec string, id int) {
DB[examId] = Cron{
Id: id,
Spec: spec,
}
}
func Query(examId string) Cron {
return DB[examId]
}