go定时任务库
在 Go 语言中,使用 Gin 框架集成定时任务非常常见,尤其是需要定期执行某些操作(如消费 Redis Stream 数据、清理缓存、监控任务等)。以下是实现定时任务的详细方法和示例代码。
1. 使用 cron 库(推荐)
Go 社区中最流行的定时任务库是 robfig/cron/v3,它支持灵活的调度表达式(如 @every 5s、0 * * * * * 等)。
步骤
安装依赖:
bashgo get github.com/robfig/cron/v3代码示例:
gopackage main import ( "github.com/gin-gonic/gin" "github.com/robfig/cron/v3" "log" "time" ) func main() { // 初始化定时任务 c := cron.New() _, err := c.AddFunc("@every 5s", func() { log.Println("定时任务执行:消费 Redis Stream 数据") // 在此处添加消费 Redis Stream 的逻辑 }) if err != nil { panic(err) } c.Start() defer c.Stop() // 初始化 Gin 路由 r := gin.Default() r.POST("/data", func(c *gin.Context) { // 接收并处理数据 // 将数据写入 Redis Stream c.JSON(200, gin.H{"status": "数据已接收"}) }) // 启动 Gin 服务 r.Run(":8080") }
关键点
- 调度表达式:
@every 5s表示每 5 秒执行一次,也可以使用标准的 Unix cron 表达式(如0 * * * * *表示每分钟执行一次)。 - 并发安全:
cron默认为每个任务启动一个新的 goroutine,适合需要并发执行的场景。 - 优雅关闭:通过
defer c.Stop()确保程序退出时停止定时任务。
2. 使用 Goroutine + time.Ticker
如果需求简单(如固定间隔执行任务),可以直接使用 time.Ticker 实现。
代码示例
go
package main
import (
"github.com/gin-gonic/gin"
"log"
"time"
)
func main() {
// 启动定时任务(每 5 秒执行一次)
go func() {
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
log.Println("定时任务执行:消费 Redis Stream 数据")
// 在此处添加消费 Redis Stream 的逻辑
}
}
}()
// 初始化 Gin 路由
r := gin.Default()
r.POST("/data", func(c *gin.Context) {
// 接收并处理数据
// 将数据写入 Redis Stream
c.JSON(200, gin.H{"status": "数据已接收"})
})
// 启动 Gin 服务
r.Run(":8080")
}关键点
- 简单场景:适合不需要复杂调度表达式的场景。
- 手动控制:需要手动管理 ticker 的生命周期(如退出时停止 ticker)。
3. 动态管理定时任务
如果需要动态添加、删除或修改定时任务,可以结合 cron 库和配置中心(如 Etcd、数据库)实现。
代码示例
go
package main
import (
"github.com/gin-gonic/gin"
"github.com/robfig/cron/v3"
"log"
"time"
)
func main() {
// 初始化 cron 调度器
c := cron.New()
c.Start()
defer c.Stop()
// 动态添加任务(例如从数据库读取配置)
jobID, err := c.AddFunc("@every 10s", func() {
log.Println("动态任务执行")
})
if err != nil {
panic(err)
}
// 动态删除任务(例如根据条件移除)
c.Remove(jobID)
// 初始化 Gin 路由
r := gin.Default()
r.POST("/data", func(c *gin.Context) {
// 接收并处理数据
c.JSON(200, gin.H{"status": "数据已接收"})
})
// 启动 Gin 服务
r.Run(":8080")
}4. 集成 Redis Stream 消费
如果需要从 Redis Stream 中消费数据,可以结合 go-redis 客户端库实现。
代码示例
go
package main
import (
"github.com/gin-gonic/gin"
"github.com/robfig/cron/v3"
"github.com/go-redis/redis/v8"
"context"
"log"
"time"
)
var rdb *redis.Client
func init() {
rdb = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
})
}
func main() {
// 初始化定时任务
c := cron.New()
_, err := c.AddFunc("@every 5s", func() {
ctx := context.Background()
// 从 Redis Stream 读取数据
streamData, err := rdb.XRead(ctx, &redis.XReadArgs{
Streams: []string{"my-stream", "0"},
Count: 10,
Block: 0,
AutoAck: false,
}).Result()
if err != nil {
log.Println("读取 Redis Stream 失败:", err)
return
}
for _, entry := range streamData[0].Messages {
log.Printf("消费数据: %s -> %v\n", entry.ID, entry.Values)
}
})
if err != nil {
panic(err)
}
c.Start()
defer c.Stop()
// 初始化 Gin 路由
r := gin.Default()
r.POST("/data", func(c *gin.Context) {
// 将数据写入 Redis Stream
err := rdb.XAdd(ctx, &redis.XAddArgs{
Stream: "my-stream",
Values: map[string]interface{}{
"data": "example",
},
}).Err()
if err != nil {
c.AbortWithStatusJSON(500, gin.H{"error": "写入 Redis 失败"})
return
}
c.JSON(200, gin.H{"status": "数据已接收"})
})
// 启动 Gin 服务
r.Run(":8080")
}总结
- 推荐方式:使用
robfig/cron/v3库,支持灵活的调度表达式,适合大多数场景。 - 简单需求:使用
time.Ticker快速实现固定间隔任务。 - 动态管理:结合配置中心动态调整任务。
- Redis 集成:使用
go-redis客户端库消费 Redis Stream 数据。
根据具体需求选择合适的方法,并确保定时任务与 Gin 服务的生命周期管理(如启动、停止)协调一致。