当前位置: 首页 > 工具软件 > gocron > 使用案例 >

【Go】Cron定时任务库

赫连宏伯
2023-12-01

前言

在项目的日常运行中,常常有需要用到定时任务的场景,比如考试即将截止的提醒,数据库的定时备份等。定时任务我们可以写脚本利用Linux的crontab命令来实现,在Go语言中也可以使用Cron定时任务库,本文将介绍此库的用法,并附上示例代码。

Cron定时任务库介绍

Cron是Go中用于设置定时任务的一个库,需要注意的是,Cron库分两个大版本,v1.2v3.0,其功能和go get地址都是不同的,注意区分。

Cron v1.2

安装:

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表达式设置说明

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

Cron v3.0

安装:

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]
}

 类似资料: