Golang:实现 cron/在特定时间执行任务

问题描述 投票:0回答:6

我一直在寻找有关如何实现一个函数的示例,该函数允许您在 Go 中的某个时间执行任务,但我找不到任何东西。

我自己实现了一个,我在答案中分享它,以便其他人可以为自己的实现提供参考。

go scheduled-tasks cron ticker
6个回答
41
投票

这是一个通用实现,它允许您设置:

  • 间歇期
  • 小时滴答
  • 分钟滴答
  • 第二个打勾

更新:(内存泄漏已修复)

import (
"fmt"
"time"
)

const INTERVAL_PERIOD time.Duration = 24 * time.Hour

const HOUR_TO_TICK int = 23
const MINUTE_TO_TICK int = 00
const SECOND_TO_TICK int = 03

type jobTicker struct {
    timer *time.Timer
}

func runningRoutine() {
    jobTicker := &jobTicker{}
    jobTicker.updateTimer()
    for {
        <-jobTicker.timer.C
        fmt.Println(time.Now(), "- just ticked")
        jobTicker.updateTimer()
    }
}

func (t *jobTicker) updateTimer() {
    nextTick := time.Date(time.Now().Year(), time.Now().Month(), 
    time.Now().Day(), HOUR_TO_TICK, MINUTE_TO_TICK, SECOND_TO_TICK, 0, time.Local)
    if !nextTick.After(time.Now()) {
        nextTick = nextTick.Add(INTERVAL_PERIOD)
    }
    fmt.Println(nextTick, "- next tick")
    diff := nextTick.Sub(time.Now())
    if t.timer == nil {
        t.timer = time.NewTimer(diff)
    } else {
        t.timer.Reset(diff)
    }
}

31
投票

万一有人偶然发现这个问题并寻求快速解决方案。 我发现了一个简洁的库,可以轻松安排工作。

链接:https://github.com/go-co-op/gocron

API 非常简单:

import (
    "fmt"
    "github.com/jasonlvhit/gocron"
)

func task() {
    fmt.Println("Task is being performed.")
}

func main() {
    s := gocron.NewScheduler()
    s.Every(2).Hours().Do(task)
    <- s.Start()
}

24
投票

@Daniele B 提供的答案不够好,正如 @Caleb 所说,该实现会泄漏内存,因为每次我们创建一个新的股票代码时,旧的股票代码永远不会被释放。

所以我包裹

time.timer
,并每次都重置它,这里是一个例子:

package main

import (
    "fmt"
    "time"
)

const INTERVAL_PERIOD time.Duration = 24 * time.Hour

const HOUR_TO_TICK int = 23
const MINUTE_TO_TICK int = 21
const SECOND_TO_TICK int = 03

type jobTicker struct {
    t *time.Timer
}

func getNextTickDuration() time.Duration {
    now := time.Now()
    nextTick := time.Date(now.Year(), now.Month(), now.Day(), HOUR_TO_TICK, MINUTE_TO_TICK, SECOND_TO_TICK, 0, time.Local)
    if nextTick.Before(now) {
        nextTick = nextTick.Add(INTERVAL_PERIOD)
    }
    return nextTick.Sub(time.Now())
}

func NewJobTicker() jobTicker {
    fmt.Println("new tick here")
    return jobTicker{time.NewTimer(getNextTickDuration())}
}

func (jt jobTicker) updateJobTicker() {
    fmt.Println("next tick here")
    jt.t.Reset(getNextTickDuration())
}

func main() {
    jt := NewJobTicker()
    for {
        <-jt.t.C
        fmt.Println(time.Now(), "- just ticked")
        jt.updateJobTicker()
    }
}

10
投票

我创建了一个实际上支持 crontab 语法的包,如果你熟悉的话,例如:

ctab := crontab.New()
ctab.AddJob("*/5 * * * *", myFunc)
ctab.AddJob("0 0 * * *", myFunc2)

软件包链接:https://github.com/mileusna/crontab


8
投票

这是另一种不需要第三方库的通用实现。

免责声明:此实现适用于UTC。为了管理时区,必须对其进行修改。

每天中午运行一次

func

  • 期间:
    time.Hour * 24
  • 偏移:
    time.Hour * 12

每天在 03:40 (00:00 + 03:40) 和 15:40 (12:00 + 03:40) 运行

func
两次。

  • 期间:
    time.Hour * 12
  • 偏移:
    time.Hour * 3 + time.Minute * 40

更新(2020-01-28):

变化:

  • context.Context
    可用于取消,使其可测试。
  • time.Ticker
    无需计算下一次执行的时间。
package main

import (
    "context"
    "time"
)

// Schedule calls function `f` with a period `p` offsetted by `o`.
func Schedule(ctx context.Context, p time.Duration, o time.Duration, f func(time.Time)) {
    // Position the first execution
    first := time.Now().Truncate(p).Add(o)
    if first.Before(time.Now()) {
        first = first.Add(p)
    }
    firstC := time.After(first.Sub(time.Now()))

    // Receiving from a nil channel blocks forever
    t := &time.Ticker{C: nil}

    for {
        select {
        case v := <-firstC:
            // The ticker has to be started before f as it can take some time to finish
            t = time.NewTicker(p)
            f(v)
        case v := <-t.C:
            f(v)
        case <-ctx.Done():
            t.Stop()
            return
        }
    }

}

原文:

package main

import (
    "time"
)

// Repeat calls function `f` with a period `d` offsetted by `o`.
func Repeat(d time.Duration, o time.Duration, f func(time.Time)) {
    next := time.Now().Truncate(d).Add(o)
    if next.Before(time.Now()) {
        next = next.Add(d)
    }

    t := time.NewTimer(next.Sub(time.Now()))

    for {
        v := <-t.C
        next = next.Add(d)
        t.Reset(next.Sub(time.Now()))
        f(v)
    }
}

0
投票

我正在使用https://github.com/ehsaniara/gointerlock。它也支持分布式系统,并具有内置的分发器锁(Redis)

import (
    "context"
    "fmt"
    "github.com/ehsaniara/gointerlock"
    "log"
    "time"
)

var job = gointerlock.GoInterval{
    Interval: 2 * time.Second,
    Arg:      myJob,
}

err := job.Run(ctx)
if err != nil {
    log.Fatalf("Error: %s", err)
}

func myJob() {
    fmt.Println(time.Now(), " - called")
}
© www.soinside.com 2019 - 2024. All rights reserved.