Skip to content

go定时任务库

在 Go 语言中,使用 Gin 框架集成定时任务非常常见,尤其是需要定期执行某些操作(如消费 Redis Stream 数据、清理缓存、监控任务等)。以下是实现定时任务的详细方法和示例代码。


1. 使用 cron 库(推荐)

Go 社区中最流行的定时任务库是 robfig/cron/v3,它支持灵活的调度表达式(如 @every 5s0 * * * * * 等)。

步骤

  1. 安装依赖

    bash
    go get github.com/robfig/cron/v3
  2. 代码示例

    go
    package 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 服务的生命周期管理(如启动、停止)协调一致。

/src/technology/dateblog/2025/05/20250526-go%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1%E5%BA%93.html