定时任务
后台系统常见的定时任务包括 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 分钟 | 重试失败消息或补偿状态 |
