Golang中Cron定时任务调度不正确的问题探讨

Golang中Cron定时任务调度不正确的问题探讨 我的配置文件中包含以下4个Cron计划

CronProcess1 = "30 13 */15 * *" # 每15天在下午1:30执行
CronProcess1Reminder = "30 13 */3 * *" # 每3天在下午1:30执行

CronProcess2 = "30 13 */3 * *" # 每3天在下午1:30执行
CronProcess2Reminder = "30 13 */1 * *" # 每天在下午1:30执行

我使用以下代码初始化了一个新的Cron任务

c := cron.New(cron.WithLogger(adapters.GetCronLoggerAdapter()), cron.WithLocation(time.UTC))

查看日志,发现不同的进程似乎没有被正确调度

schedule [[now 2021-06-28 06:27:26.8877311 +0000 UTC entry 1 next 2021-07-01 13:30:00 +0000 UTC]] 
schedule [[now 2021-06-28 06:27:26.8877311 +0000 UTC entry 2 next 2021-06-28 13:30:00 +0000 UTC]]
schedule [[now 2021-06-28 06:27:26.8877311 +0000 UTC entry 3 next 2021-06-28 13:30:00 +0000 UTC]]
schedule [[now 2021-06-28 06:27:26.8877311 +0000 UTC entry 4 next 2021-06-28 13:30:00 +0000 UTC]]

第一条记录的下次执行时间应该是2021-07-13 13:30:00。其他记录的时间也不正确。 有什么想法可能导致这个问题吗?


更多关于Golang中Cron定时任务调度不正确的问题探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

更多关于Golang中Cron定时任务调度不正确的问题探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


看来你的系统时区配置似乎有问题。

如果它与你的地理位置不匹配,那么任何基于时区的计算当然都会出错。

你所在的时区是哪个?你所在时区的早上6:30是否等同于UTC时间的下午1:30?

我认为这可能与我的时区设置有关。查看我的笔记本电脑,显示我处于 (UTC +3:00) 时区。 但通过谷歌查询我的时区,却显示为东部夏令时 (GMT-4)。

另外,重新运行了日志。现在是下午2点,但日志显示:

[[now 2021-06-29 11:02:04.9369524 +0000 UTC entry 1 next 2021-07-01 12:30:00 +0000 UTC]]

对应的是 30 12 */15 * *

问题出在Cron表达式的理解上。Go的cron库使用的是标准Unix cron格式,其中*/15在"天"字段中表示"每15天",但实际行为可能与你期望的不同。

标准cron的*/15在"天"字段中表示:在满足其他时间条件的情况下,在每月的第1、16、31天执行(如果月份有31天)。对于你的需求,应该使用更明确的表达式。

以下是修正后的代码示例:

package main

import (
    "fmt"
    "time"
    
    "github.com/robfig/cron/v3"
)

func main() {
    c := cron.New(cron.WithLocation(time.UTC))
    
    // 每15天在下午1:30执行 - 使用明确的日期范围
    // 方法1: 使用多个表达式
    _, err1 := c.AddFunc("30 13 1,16 * *", func() {
        fmt.Println("Process1 executed at", time.Now().UTC())
    })
    
    // 方法2: 如果需要更灵活的15天间隔,需要自定义调度器
    _, err2 := c.AddFunc("30 13 */3 * *", func() {
        fmt.Println("Process1Reminder executed at", time.Now().UTC())
    })
    
    _, err3 := c.AddFunc("30 13 */3 * *", func() {
        fmt.Println("Process2 executed at", time.Now().UTC())
    })
    
    _, err4 := c.AddFunc("30 13 * * *", func() {
        fmt.Println("Process2Reminder executed at", time.Now().UTC())
    })
    
    if err1 != nil || err2 != nil || err3 != nil4 != nil {
        fmt.Println("Error adding cron jobs")
        return
    }
    
    c.Start()
    
    // 保持程序运行
    select {}
}

对于真正的"每15天"调度(从特定日期开始),你需要自定义调度逻辑:

package main

import (
    "fmt"
    "time"
    
    "github.com/robfig/cron/v3"
)

type CustomSchedule struct {
    intervalDays int
    startDate    time.Time
    hour         int
    minute       int
}

func (cs *CustomSchedule) Next(t time.Time) time.Time {
    // 计算从开始日期到当前时间的天数差
    daysSinceStart := int(t.Sub(cs.startDate).Hours() / 24)
    
    // 计算下一个执行日期
    nextDays := ((daysSinceStart / cs.intervalDays) + 1) * cs.intervalDays
    nextDate := cs.startDate.AddDate(0, 0, nextDays)
    
    // 设置具体执行时间
    nextTime := time.Date(
        nextDate.Year(),
        nextDate.Month(),
        nextDate.Day(),
        cs.hour,
        cs.minute,
        0, 0,
        nextDate.Location(),
    )
    
    return nextTime
}

func main() {
    c := cron.New(cron.WithLocation(time.UTC))
    
    // 设置开始日期(例如从2021-06-01开始)
    startDate := time.Date(2021, 6, 1, 0, 0, 0, 0, time.UTC)
    
    // 每15天执行
    customSchedule1 := &CustomSchedule{
        intervalDays: 15,
        startDate:    startDate,
        hour:         13,
        minute:       30,
    }
    
    c.Schedule(customSchedule1, cron.FuncJob(func() {
        fmt.Println("Process1 (every 15 days) executed at", time.Now().UTC())
    }))
    
    c.Start()
    
    // 测试输出接下来几次执行时间
    testTime := time.Date(2021, 6, 28, 6, 27, 26, 0, time.UTC)
    for i := 0; i < 5; i++ {
        nextTime := customSchedule1.Next(testTime)
        fmt.Printf("Next execution %d: %v\n", i+1, nextTime)
        testTime = nextTime
    }
    
    select {}
}

标准cron的*/n在"天"字段中的行为是:在满足月份有效天数的前提下,在1号、1+n、1+2n…号执行。对于"每3天"的需求,*/3会在一月的1、4、7…号执行,而不是从当前日期开始每3天执行。

回到顶部