Skip to content

Latest commit

 

History

History
222 lines (157 loc) · 4.67 KB

File metadata and controls

222 lines (157 loc) · 4.67 KB

Cron - Go 语言定时任务调度器

English

一个功能强大的 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() // 等待所有运行中的任务完成

使用 Job 接口

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()

FakeClock 单元测试

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

NTP时间源

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()选项传入自定义时间源即可。

Cron 表达式示例(5字段)

30 * * * *        每小时的第 30 分钟
0 0 * * *         每天午夜
0 0 1 * *         每月第一天午夜
0 0 * * 0         每周日午夜
*/15 * * * *      每 15 分钟
0 9-17 * * *      每天 9-17 点的整点

许可证

MIT License