Go 语言定时任务实现方式详解
本文内容基于 AI 生成结果整理,可能包含不准确信息,仅供参考使用。
一、使用标准库 time 包实现定时任务
1. time.Ticker(周期性定时器)
time.Ticker 适合需要周期性执行的任务。
go
package main
import (
"fmt"
"time"
)
func main() {
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop() // 确保资源释放
for {
select {
case <-ticker.C:
fmt.Println("定时任务执行:", time.Now().Format("2006-01-02 15:04:05"))
}
}
}
特点 :
- 周期性触发
- 适合简单定时场景
- 需要手动停止避免资源泄漏
2. time.Timer(单次定时器)
time.Timer 适合只需要执行一次的任务。
go
package main
import (
"fmt"
"time"
)
func main() {
timer := time.NewTimer(2 * time.Second)
<-timer.C
fmt.Println("定时任务执行:", time.Now().Format("2006-01-02 15:04:05"))
// 可重置实现循环
timer.Reset(2 * time.Second)
<-timer.C
fmt.Println("第二次执行:", time.Now().Format("2006-01-02 15:04:05"))
}
特点 :
- 单次触发
- 可通过 Reset 方法实现循环
- 需要手动管理生命周期
3. time.AfterFunc(异步定时任务)
go
package main
import (
"fmt"
"time"
)
func task() {
fmt.Println("定时任务执行:", time.Now().Format("2006-01-02 15:04:05"))
}
func main() {
timer := time.AfterFunc(3*time.Second, task)
time.Sleep(4 * time.Second)
// timer.Stop() // 可取消未执行的任务
}
特点 :
- 异步执行
- 适合不需要等待结果的场景
- 可取消未执行的任务
二、使用第三方 cron 库(推荐)
对于复杂的定时任务需求,推荐使用 github.com/robfig/cron/v3 库。
基本用法
go
package main
import (
"fmt"
"time"
"github.com/robfig/cron/v3"
)
func main() {
c := cron.New()
// 每秒执行
_, _ = c.AddFunc("@every 1s", func() {
fmt.Println("每秒执行:", time.Now().Format("2006-01-02 15:04:05"))
})
// 每分钟执行
_, _ = c.AddFunc("* * * * *", func() {
fmt.Println("每分钟执行:", time.Now().Format("2006-01-02 15:04:05"))
})
c.Start()
select {} // 阻止程序退出
}
生产环境推荐用法
go
package main
import (
"log"
"time"
"github.com/robfig/cron/v3"
)
func main() {
// 带日志记录的cron实例
c := cron.New(cron.WithLogger(
cron.VerbosePrintfLogger(log.New(log.Writer(), "cron: ", log.LstdFlags))))
_, err := c.AddFunc("@every 1s", func() {
defer func() {
if r := recover(); r != nil {
log.Printf("任务执行出错: %v", r)
}
}()
log.Println("任务开始执行")
time.Sleep(500 * time.Millisecond)
log.Println("任务执行完成")
})
if err != nil {
log.Fatal("添加任务失败:", err)
}
c.Start()
defer c.Stop()
time.Sleep(5 * time.Second)
log.Println("程序退出")
}
优势 :
- 支持完整的 cron 表达式
- 提供任务执行日志
- 内置错误恢复机制
- 支持秒级精度(v3 版本)
优雅关闭写法
go
package cron
import (
"strings"
"time"
wechat "gitee.com/iswleii/wechat-weather/internal/wechat/service/types"
"gitee.com/iswleii/wechat-weather/internal/user"
"gitee.com/iswleii/wechat-weather/internal/weather"
"github.com/robfig/cron/v3"
"go.uber.org/zap"
)
// WeatherCron 天气定时任务结构体
type WeatherCron struct {
weatherService weather.Service
userService user.Service
cron *cron.Cron
logger *zap.Logger
wechatService wechat.Service
}
// NewWeatherCron 创建新的天气定时任务实例
func NewWeatherCron(
ws weather.Service,
us user.Service,
logger *zap.Logger,
wechatService wechat.Service,
) *WeatherCron {
return &WeatherCron{
weatherService: ws,
userService: us,
cron: cron.New(cron.WithSeconds()), // 使用秒级精度
logger: logger,
wechatService: wechatService,
}
}
// Start 启动定时任务
func (wc *WeatherCron) Start() {
// 每天早上7点发送天气提醒
_, err := wc.cron.AddFunc("0 0 7 * * *", func() {
wc.sendWeatherNotifications("早安!今日天气提醒")
})
if err != nil {
wc.logger.Error("添加早晨天气定时任务失败", zap.Error(err))
}
// 每天晚上20点发送天气提醒
_, err = wc.cron.AddFunc("0 0 20 * * *", func() {
wc.sendWeatherNotifications("晚安!明日天气预告")
})
if err != nil {
wc.logger.Error("添加晚间天气定时任务失败", zap.Error(err))
}
// 启动定时任务
wc.cron.Start()
wc.logger.Info("天气定时任务已启动")
}
// Stop 停止定时任务
func (wc *WeatherCron) Stop() {
if wc.cron != nil {
ctx := wc.cron.Stop()
// 等待所有正在执行的任务完成
select {
case <-ctx.Done():
wc.logger.Info("天气定时任务已停止")
case <-time.After(5 * time.Second):
wc.logger.Warn("天气定时任务停止超时")
}
}
}
// sendWeatherNotifications 发送天气通知给所有订阅用户
func (wc *WeatherCron) sendWeatherNotifications(prefix string) {}
三、定时任务最佳实践
错误处理 :任务函数内部应该捕获 panic
日志记录 :记录任务开始、结束和错误信息
资源释放 :使用 defer 确保定时器/调度器正确关闭
并发控制 :长时间任务考虑使用 goroutine 和 sync.WaitGroup
配置化 :将定时配置提取到配置文件中
四、方案选型建议
方案 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
time.Ticker | 简单周期性任务 | 标准库,无需依赖 | 功能简单 |
time.Timer | 单次或可重置任务 | 灵活可控 | 需要手动管理 |
time.AfterFunc | 异步单次任务 | 使用简单 | 功能有限 |
cron 库 | 复杂定时需求 | 功能强大 | 需要引入第三方库 |
对于大多数生产环境,推荐使用 cron 库,它提供了更丰富的功能和更好的可维护性。
五、常见问题解答
Q:定时任务执行时间过长会影响下次执行吗?
A:对于 time.Ticker 和 cron 库,默认会等待当前任务完成再开始下次计时。如果需要并发执行,可以在任务内启动 goroutine。
Q:如何实现分布式环境下的定时任务?
A:标准方案需要借助分布式锁或使用专门的分布式任务调度系统。单机方案无法直接扩展到分布式环境。
Q:定时任务执行失败后如何重试?
A:可以在任务函数内部实现重试逻辑,或者使用支持重试的任务队列系统。