一个功能强大的 Go 语言 cron 表达式解析器和任务调度器,支持标准 cron 语法、秒级精度、时区控制以及自定义时钟机制。
Frok from robfig/cron
- 标准 Cron 语法 - 默认支持标准的 5 字段 cron 表达式(分、时、日、月、周);可选的秒级字段支持
- 预定义调度 - 支持 @hourly、@daily、@weekly、@monthly、@yearly 等便捷表达式
- 固定间隔 - 使用 @every 语法实现固定时间间隔调度
- 时区支持 - 全局和单任务级别的时区配置
- 动态管理 - 运行时添加、删除和查看任务
- 线程安全 - 所有操作都经过精心设计以确保并发安全
- 多种时钟 - 支持默认系统时钟、模拟时钟、自定义时钟
通过 Clock 接口自由控制时间,支持:
- DefaultClock - 使用系统时钟(默认)
- FakeClock - 模拟时钟用于单元测试
- 自定义实现 - 如传入NTP精确网络时间
go get github.com/banbox/cron/v3要求 Go 1.11 或更高版本(使用 Go Modules)。
package main
import (
"fmt"
"github.com/banbox/cron/v3"
)
func main() {
c := cron.New()
// 每小时的第 30 分钟执行
c.AddFunc("30 * * * *", func() {
fmt.Println("每小时执行一次")
})
// 启动调度器
c.Start()
// 阻塞主程序
select {}
}// 支持秒级精度
c := cron.New(cron.WithSeconds())
// 每秒执行
c.AddFunc("* * * * * *", func() {
fmt.Println("每秒执行")
})
// @hourly, @daily, @weekly, @monthly, @yearly
c.AddFunc("@hourly", func() { fmt.Println("每小时") })
// 每 1 小时 30 分钟执行一次
c.AddFunc("@every 1h30m", func() {
fmt.Println("每 90 分钟执行")
})
c.Start()import "time"
// 全局时区设置
loc, _ := time.LoadLocation("Asia/Shanghai")
c := cron.New(cron.WithClock(cron.NewDefaultClock(loc)))
// 单个任务指定时区
c.AddFunc("CRON_TZ=Asia/Tokyo 0 6 * * *", func() {
fmt.Println("东京时间每天 6 点执行")
})
c.Start()c := cron.New()
// 添加任务并获取 ID
id, _ := c.AddFunc("@every 1m", func() {
fmt.Println("任务执行")
})
c.Start()
// 查看所有任务
for _, entry := range c.Entries() {
fmt.Printf("任务 %d: 下次执行时间 %v\n", entry.ID, entry.Next)
}
// 移除任务
c.Remove(id)
// 停止调度器
ctx := c.Stop()
<-ctx.Done() // 等待所有运行中的任务完成type MyJob struct {
Name string
}
func (j MyJob) Run() {
fmt.Printf("执行任务: %s\n", j.Name)
}
c := cron.New()
c.AddJob("@every 1m", MyJob{Name: "数据同步"})
c.Start()func TestMultipleExecutions(t *testing.T) {
startTime := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
fakeClock := cron.NewFakeClock(time.UTC, startTime)
c := cron.New(cron.WithClock(fakeClock), cron.WithSeconds())
count := 0
c.AddFunc("0 0 * * * *", func() { count++ })
c.Start()
defer c.Stop()
// 推进 24 小时,应该执行 24 次
c.RunTo(startTime.Add(24 * time.Hour))
if count != 24 {
t.Errorf("期望执行 24 次,实际 %d 次", count)
}
}cron内置了bntp支持,可以使用NTP作为时间源(标准网络时间),确保在正确的网络时间下精确触发任务。
package main
import (
"fmt"
"time"
"github.com/banbox/cron/v3"
"github.com/banbox/bntp"
)
func main() {
// 创建使用 ntp 时间源的 cron 调度器
loc, _ := time.LoadLocation("Asia/Shanghai")
ntpClock := NewNtpClock(loc, bntp.LangGlobal)
c := cron.New(cron.WithClock(ntpClock))
// 添加任务
c.AddFunc("* * * * *", func() {
realTime := bntp.UTCStamp()
sysTime := time.Now().UnixMilli()
fmt.Println("system: %d, real: %d", sysTime, realTime)
})
c.Start()
// 阻塞主程序
select {}
}您也可使用其他自定义时间源,但需实现Clock接口,可参考NtpClock。
然后使用cron.WithClock()选项传入自定义时间源即可。
30 * * * * 每小时的第 30 分钟
0 0 * * * 每天午夜
0 0 1 * * 每月第一天午夜
0 0 * * 0 每周日午夜
*/15 * * * * 每 15 分钟
0 9-17 * * * 每天 9-17 点的整点
MIT License