# go 的定时任务包 cron

robfig/cron 是 Go 语言实现的开源定时任务调度框架,核心代码是巧妙的使用 chan + select + for 实现了一个轻量级调度协程,不但语法简洁,而且具有很好的性能。

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

  1. v1.2 官方文档:https://pkg.go.dev/github.com/robfig/cron
  2. v3 官方文档: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 {}
}
最后修改:2023 年 12 月 30 日
如果觉得我的文章对你有用,请随意赞赏