Skip to content

定时任务

后台系统常见的定时任务包括 Token 清理、日志归档、报表统计、缓存预热、消息补偿和数据同步。youlai-gin 推荐优先使用 robfig/cron + 分布式锁 的方案:单体部署时足够轻量,多实例部署时也能避免任务重复执行。

方案选型

方案适合场景说明
robfig/cron应用内轻量任务Go 项目集成简单,适合中小型后台
系统 Cron运维脚本、HTTP 触发与应用解耦,但状态和日志需要额外治理
XXL-JOB / Asynq大量任务、可视化调度、队列任务适合任务规模较大或需要任务平台的系统
Kubernetes CronJob云原生定时任务适合容器化部署和独立任务进程

普通后台优先选择 robfig/cron;如果任务需要重试、队列、可视化和人工补偿,再升级到任务平台。

推荐目录

text
internal/scheduler/
  scheduler.go        # 调度器初始化与生命周期
  lock.go             # Redis 分布式锁
  jobs/
    log_clean.go      # 日志清理
    token_clean.go    # Token 清理
    report_stat.go    # 报表统计

调度模块只负责任务注册和执行编排,具体业务逻辑仍放在对应 Service 中,避免任务代码直接写数据库细节。

安装依赖

bash
go get github.com/robfig/cron/v3

初始化调度器

go
package scheduler

import "github.com/robfig/cron/v3"

type Scheduler struct {
    cron *cron.Cron
}

func NewScheduler() *Scheduler {
    return &Scheduler{
        cron: cron.New(cron.WithSeconds()),
    }
}

func (s *Scheduler) Register() error {
    if _, err := s.cron.AddFunc("0 0 3 * * *", CleanOperationLogs); err != nil {
        return err
    }
    if _, err := s.cron.AddFunc("0 */10 * * * *", CleanExpiredTokens); err != nil {
        return err
    }
    return nil
}

func (s *Scheduler) Start() {
    s.cron.Start()
}

func (s *Scheduler) Stop() {
    ctx := s.cron.Stop()
    <-ctx.Done()
}

表达式示例:

text
0 */10 * * * *   每 10 分钟执行一次
0 0 3 * * *      每天凌晨 3 点执行
0 0 2 * * 1      每周一凌晨 2 点执行

接入启动流程

main.go 中,建议放在数据库、Redis 初始化之后:

go
s := scheduler.NewScheduler()
if err := s.Register(); err != nil {
    log.Fatalf("定时任务注册失败: %v", err)
}
s.Start()
defer s.Stop()

如果任务依赖 Redis 分布式锁,必须保证 Redis 初始化成功后再启动调度器。

分布式锁

多实例部署时,同一个任务可能被多个实例同时触发。建议用 Redis 锁保护任务入口:

go
func RunWithLock(ctx context.Context, key string, ttl time.Duration, fn func() error) error {
    ok, err := redis.Client.SetNX(ctx, key, "1", ttl).Result()
    if err != nil || !ok {
        return err
    }
    defer redis.Client.Del(ctx, key)
    return fn()
}

任务执行:

go
func CleanExpiredTokens() {
    ctx := context.Background()
    _ = RunWithLock(ctx, "scheduler:token-clean", 5*time.Minute, func() error {
        return authService.CleanExpiredTokens(ctx)
    })
}

锁的 TTL 要大于任务正常执行时间,任务本身也要保持幂等。

实践规范

  • 任务必须幂等,重复执行不能产生脏数据。
  • 长任务不要阻塞 API 请求链路。
  • 每个任务记录开始时间、结束时间、耗时和结果。
  • 任务失败要有错误日志,关键任务要有告警。
  • 任务业务逻辑放 Service,调度器只负责编排。
  • 多实例部署必须加分布式锁。

常见任务

任务周期说明
Token 清理10 分钟清理过期会话、黑名单或临时键
操作日志归档每天删除或归档历史操作日志
报表统计每天/每小时生成运营统计数据
缓存预热启动后/定时加载系统配置、字典、权限缓存
消息补偿1 分钟重试失败消息或补偿状态

基于 MIT 许可发布 · 如需部署协助或二开定制,请查看 支持与合作