每小时运行后台任务的Golang实现方案
每小时运行后台任务的Golang实现方案 我知道我们通常可以通过两种常见的时间调度方式在后台运行任务:
- 在特定的时间段之后,例如 5 分钟后。
- 每隔特定的时间间隔,例如每 5 分钟。
这里 有一个简单的例子:
package main
import (
"fmt"
"time"
)
func bgTask() {
ticker := time.NewTicker(1 * time.Second)
for _ = range ticker.C {
fmt.Println("Tock")
}
}
func main() {
fmt.Println("Go Tickers Tutorial")
go bgTask()
// This print statement will be executed before
// the first `tock` prints in the console
fmt.Println("The rest of my application can continue")
// here we use an empty select{} in order to keep
// our main function alive indefinitely as it would
// complete before our backgroundTask has a chance
// to execute if we didn't.
select {}
}
我的需求有所不同,我希望在每天的特定时间运行一个活动,例如每小时(12:00, 1:00, 2:00, 3:00, …),或者每天午夜(即正好在午夜 00:00)。
更多关于每小时运行后台任务的Golang实现方案的实战教程也可以访问 https://www.itying.com/category-94-b0.html
3 回复
更多关于每小时运行后台任务的Golang实现方案的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Sibert:
我猜你必须使用 http://github.com/robfig/cron
谢谢,我找到了下面这段无需第三方库即可工作的代码:
package main
import (
"context"
"fmt"
"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
}
}
}
func main() {
ctx := context.Background() // .TODO()
fmt.Println("Let's start:", time.Now())
current := time.Now()
hr := current.Hour()
fmt.Printf("The stated hour "+
"within the day is: %v\n", hr)
// Schedule(ctx, time.Minute*2, time.Minute, fn()) Run every 2 minutes, starting 1 minute after the first run of this code
// if the code started 52:41.26, then first run will be at 53:00 followed by another run at 55:00, and so on
// Schedule(ctx, time.Hour, time.Hour, func(t time.Time){}) Run every head of each hour, like 20:00:00, 21:00:00, ...
Schedule(ctx, time.Hour, time.Hour, func(t time.Time) {
fmt.Println("Hi, it is:", time.Now())
})
}
目前的输出:
PS D:\Desktop> go run scheduler.go
Let's start: 2022-05-18 19:04:25.9967368 +0300 +03 m=+0.001233101
The stated hour within the day is: 19
Hi, it is: 2022-05-18 20:00:00.0322164 +0300 +03 m=+3334.010983001
Hi, it is: 2022-05-18 21:00:00.0447332 +0300 +03 m=+6934.015473401
Hi, it is: 2022-05-18 22:00:00.053622 +0300 +03 m=+10534.022922201
Hi, it is: 2022-05-18 23:00:00.0611189 +0300 +03 m=+14134.020641301
Hi, it is: 2022-05-19 00:00:00.0722724 +0300 +03 m=+17734.012720101
Hi, it is: 2022-05-19 01:00:00.0873397 +0300 +03 m=+21334.023790201
Hi, it is: 2022-05-19 02:00:00.1060637 +0300 +03 m=+24934.025385401
Hi, it is: 2022-05-19 03:00:00.1107291 +0300 +03 m=+28534.025584401
Hi, it is: 2022-05-19 04:00:00.1168757 +0300 +03 m=+32134.018658401
Hi, it is: 2022-05-19 05:00:00.1334013 +0300 +03 m=+35734.024843001
Hi, it is: 2022-05-19 06:00:00.1393515 +0300 +03 m=+39334.024025801
Hi, it is: 2022-05-19 07:00:00.1362642 +0300 +03 m=+42934.015270601
Hi, it is: 2022-05-19 08:00:00.1512836 +0300 +03 m=+46534.025222701
Hi, it is: 2022-05-19 09:00:00.1654086 +0300 +03 m=+50134.022125001
Hi, it is: 2022-05-19 10:00:00.1721446 +0300 +03 m=+53734.020983001
对于在每天特定时间运行后台任务的需求,Go语言中有几种实现方案。以下是两种常用方法:
方案一:使用time.Ticker配合时间计算
package main
import (
"fmt"
"time"
)
func scheduleTaskAtSpecificTime(hour, minute, second int) {
for {
now := time.Now()
next := time.Date(
now.Year(), now.Month(), now.Day(),
hour, minute, second, 0, now.Location(),
)
if now.After(next) {
next = next.Add(24 * time.Hour)
}
duration := next.Sub(now)
fmt.Printf("Next execution in: %v\n", duration)
<-time.After(duration)
// 执行任务
fmt.Printf("Task executed at %s\n", time.Now().Format("15:04:05"))
// 等待一秒钟避免立即重复执行
time.Sleep(1 * time.Second)
}
}
func main() {
// 每小时运行(在每小时的第0分钟第0秒)
go scheduleTaskAtSpecificTime(0, 0, 0)
// 或者每天午夜运行
// go scheduleTaskAtSpecificTime(0, 0, 0)
select {}
}
方案二:使用cron表达式(推荐)
使用第三方库如robfig/cron可以更灵活地处理复杂调度:
package main
import (
"fmt"
"time"
"github.com/robfig/cron/v3"
)
func main() {
c := cron.New(cron.WithSeconds()) // 支持秒级精度
// 每小时运行(每小时的第0分钟第0秒)
_, err := c.AddFunc("0 0 * * * *", func() {
fmt.Printf("Hourly task executed at %s\n",
time.Now().Format("2006-01-02 15:04:05"))
})
if err != nil {
fmt.Printf("Error adding cron job: %v\n", err)
return
}
// 每天午夜运行
_, err = c.AddFunc("0 0 0 * * *", func() {
fmt.Printf("Midnight task executed at %s\n",
time.Now().Format("2006-01-02 15:04:05"))
})
// 每5分钟运行
_, err = c.AddFunc("0 */5 * * * *", func() {
fmt.Printf("Every 5 minutes task executed at %s\n",
time.Now().Format("2006-01-02 15:04:05"))
})
c.Start()
// 保持主程序运行
select {}
}
方案三:使用time.Ticker的精确小时调度
package main
import (
"fmt"
"time"
)
func hourlyTask() {
// 计算到下一个整点的时间
now := time.Now()
nextHour := now.Truncate(time.Hour).Add(time.Hour)
duration := nextHour.Sub(now)
fmt.Printf("First execution in: %v\n", duration)
// 等待到下一个整点
<-time.After(duration)
// 创建每小时触发的ticker
ticker := time.NewTicker(time.Hour)
defer ticker.Stop()
for t := range ticker.C {
fmt.Printf("Hourly task executed at %s\n",
t.Format("2006-01-02 15:04:05"))
}
}
func main() {
go hourlyTask()
select {}
}
方案四:带错误处理的完整示例
package main
import (
"fmt"
"log"
"time"
"github.com/robfig/cron/v3"
)
func taskWrapper(taskName string, taskFunc func()) func() {
return func() {
start := time.Now()
fmt.Printf("[%s] Starting at %s\n",
taskName, start.Format("15:04:05"))
defer func() {
if r := recover(); r != nil {
log.Printf("[%s] Panic recovered: %v", taskName, r)
}
}()
taskFunc()
duration := time.Since(start)
fmt.Printf("[%s] Completed in %v\n", taskName, duration)
}
}
func main() {
c := cron.New(
cron.WithLogger(
cron.VerbosePrintfLogger(log.New(log.Writer(),
"cron: ", log.LstdFlags)),
),
)
// 每小时运行
c.AddFunc("@hourly", taskWrapper("hourly-task", func() {
// 实际业务逻辑
fmt.Println("Processing hourly data...")
time.Sleep(100 * time.Millisecond) // 模拟任务执行
}))
// 每天午夜运行
c.AddFunc("@midnight", taskWrapper("midnight-task", func() {
fmt.Println("Processing daily reports...")
time.Sleep(200 * time.Millisecond)
}))
// 自定义时间:每天10:30运行
c.AddFunc("0 30 10 * * *", taskWrapper("custom-time", func() {
fmt.Println("Running at 10:30 AM daily...")
}))
c.Start()
// 优雅关闭
defer c.Stop()
select {}
}
安装cron库
go get github.com/robfig/cron/v3
推荐使用robfig/cron方案,它提供了更丰富的调度表达式和更好的错误处理机制,特别适合生产环境使用。

