Go Cron 定时任务完全指南:从入门到生产实践
peakchao
2025-12-13 05:47
16 次浏览
0 条评论
定时任务概述
什么是定时任务
定时任务(Scheduled Task)是指在预定时间或按照固定时间间隔自动执行的程序任务。
┌─────────────────────────────────────────────────────────────────┐
│ 定时任务应用场景 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 📊 数据处理 📧 通知服务 │
│ ├── 数据备份 ├── 邮件发送 │
│ ├── 日志清理 ├── 消息推送 │
│ ├── 报表生成 └── 定时提醒 │
│ └── 数据同步 │
│ │
│ 🔄 系统维护 💰 业务处理 │
│ ├── 缓存刷新 ├── 订单超时处理 │
│ ├── 会话清理 ├── 账单生成 │
│ ├── 健康检查 ├── 库存同步 │
│ └── 证书更新 └── 积分过期处理 │
│ │
└─────────────────────────────────────────────────────────────────┘Go 中的定时任务方案
| 方案 | 特点 | 适用场景 |
|---|---|---|
time.Ticker | 标准库,固定间隔执行 | 简单的心跳检测、定期轮询 |
time.AfterFunc | 标准库,延迟执行一次 | 超时处理、延迟任务 |
robfig/cron | 功能丰富,支持 cron 表达式 | 复杂的定时调度需求 |
go-co-op/gocron | 更简洁的 API | 中等复杂度的定时任务 |
为什么选择 robfig/cron
┌─────────────────────────────────────────────────────────────────┐
│ robfig/cron 优势 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ✅ 标准 Cron 表达式支持 ✅ 秒级精度 │
│ ✅ 时区支持 ✅ 任务链与中间件 │
│ ✅ 并发安全 ✅ 动态添加/删除任务 │
│ ✅ 活跃的社区维护 ✅ 生产环境验证 │
│ │
└─────────────────────────────────────────────────────────────────┘robfig/cron 库介绍
安装
# 安装 v3 版本(推荐)
版本差异
| 特性 | v2 | v3 |
|---|---|---|
| 默认格式 | 6 字段(含秒) | 5 字段(标准 cron) |
| 秒级支持 | 默认支持 | 需要 WithSeconds() 选项 |
| 时区 | 全局设置 | 可在选项中配置 |
| 中间件 | 不支持 | 支持 Job Wrapper(中间件) |
| 返回值 | 无任务 ID | 返回 EntryID |
快速开始
package main
import (
"fmt"
"time"
"github.com/robfig/cron/v3"
)
func main() Cron 表达式详解
标准 Cron 表达式(5 字段)
┌─────────────────────────────────────────────────────────────────┐
│ Cron 表达式格式(5 字段) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────── 分钟 (0 - 59) │
│ │ ┌───────────── 小时 (0 - 23) │
│ │ │ ┌───────────── 日 (1 - 31) │
│ │ │ │ ┌───────────── 月 (1 - 12) │
│ │ │ │ │ ┌───────────── 星期 (0 - 6, 0 = 周日) │
│ │ │ │ │ │ │
│ │ │ │ │ │ │
│ * * * * * │
│ │
│ 示例: │
│ "30 8 * * *" → 每天 08:30 执行 │
│ "0 */2 * * *" → 每 2 小时执行 │
│ "0 9 * * 1-5" → 周一到周五 09:00 执行 │
│ │
└─────────────────────────────────────────────────────────────────┘扩展 Cron 表达式(6 字段,含秒)
┌─────────────────────────────────────────────────────────────────┐
│ Cron 表达式格式(6 字段) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────── 秒 (0 - 59) │
│ │ ┌───────────── 分钟 (0 - 59) │
│ │ │ ┌───────────── 小时 (0 - 23) │
│ │ │ │ ┌───────────── 日 (1 - 31) │
│ │ │ │ │ ┌───────────── 月 (1 - 12) │
│ │ │ │ │ │ ┌───────────── 星期 (0 - 6) │
│ │ │ │ │ │ │ │
│ * * * * * * │
│ │
│ 示例: │
│ "0 30 8 * * *" → 每天 08:30:00 执行 │
│ "*/5 * * * * *" → 每 5 秒执行 │
│ "0 0 0 1 * *" → 每月 1 号 00:00:00 执行 │
│ │
└─────────────────────────────────────────────────────────────────┘特殊字符说明
| 字符 | 说明 | 示例 | 含义 |
|---|---|---|---|
* | 匹配所有值 | * * * * * | 每分钟 |
, | 列举多个值 | 0,30 * * * * | 每小时的 0 分和 30 分 |
- | 范围 | 0 9-17 * * * | 9 点到 17 点的每小时 |
/ | 步长 | */15 * * * * | 每 15 分钟 |
? | 不指定(日/星期) | 0 0 * * ? | 任意星期 |
L | 最后 | 0 0 L * * | 每月最后一天 |
W | 最近工作日 | 0 0 15W * * | 每月 15 号最近的工作日 |
# | 第 N 个星期几 | 0 0 * * 5#3 | 每月第 3 个周五 |
常用表达式示例
// 常用 Cron 表达式集合
var CronExpressions = map[string]string预定义表达式
robfig/cron 支持一些预定义的表达式:
| 预定义 | 等效表达式 | 说明 |
|---|---|---|
@yearly / @annually | 0 0 1 1 * | 每年 1 月 1 日 00:00 |
@monthly | 0 0 1 * * | 每月 1 日 00:00 |
@weekly | 0 0 * * 0 | 每周日 00:00 |
@daily / @midnight | 0 0 * * * | 每天 00:00 |
@hourly | 0 * * * * | 每小时整点 |
@every <duration> | - | 固定间隔,如 @every 1h30m |
// 使用预定义表达式
c.AddFunc("@daily", dailyTask)
c.AddFunc("@every 30s", heartbeat)
c.AddFunc("@hourly", cleanupCache)基础使用方法
方式一:使用 AddFunc 添加函数任务
package main
import (
"fmt"
"log"
"time"
"github.com/robfig/cron/v3"
)
func main() 方式二:使用 AddJob 添加 Job 接口
package main
import (
"fmt"
"log"
"sync/atomic"
"time"
"github.com/robfig/cron/v3"
)
// 定义任务结构体,实现 cron.Job 接口
type DataSyncJob struct
// 实现 Run 方法
func ()
func main() 方式三:使用 FuncJob 包装函数
package main
import (
"log"
"github.com/robfig/cron/v3"
)
func main() 启用秒级精度
package main
import (
"log"
"time"
"github.com/robfig/cron/v3"
)
func main() 高级配置选项
cron.New() 配置选项详解
// 所有可用的配置选项
c := cron.New(
cron.WithSeconds(), // 启用秒级解析(6字段)
cron.WithLocation(loc), // 设置时区
cron.WithParser(parser), // 自定义解析器
cron.WithChain(wrappers...), // 添加中间件
cron.WithLogger(logger), // 自定义日志
)时区配置
package main
import (
"log"
"time"
"github.com/robfig/cron/v3"
)
func main() 自定义解析器
package main
import (
"log"
"github.com/robfig/cron/v3"
)
func main() 解析器标志位说明:
| 标志位 | 说明 |
|---|---|
cron.Second | 启用秒字段 |
cron.Minute | 启用分钟字段 |
cron.Hour | 启用小时字段 |
cron.Dom | 启用日期字段 |
cron.Month | 启用月份字段 |
cron.Dow | 启用星期字段 |
cron.DowOptional | 星期字段可选 |
cron.Descriptor | 启用预定义描述符 |
中间件(Job Wrapper)
中间件可以在任务执行前后添加额外逻辑,如日志、恢复 panic、延迟执行等。
内置中间件
package main
import (
"log"
"github.com/robfig/cron/v3"
)
func main() 内置中间件说明:
| 中间件 | 作用 |
|---|---|
Recover | 捕获任务中的 panic,记录日志并继续运行 |
SkipIfStillRunning | 如果任务上次执行还未完成,跳过本次执行 |
DelayIfStillRunning | 如果任务上次执行还未完成,等待完成后立即执行 |
自定义中间件
package main
import (
"context"
"log"
"time"
"github.com/robfig/cron/v3"
)
// 执行耗时统计中间件
func TimingWrapper() cron.JobWrapper
// 超时控制中间件
func TimeoutWrapper(timeout time.Duration) cron.JobWrapper
// 重试中间件
func RetryWrapper(maxRetries int, delay time.Duration) cron.JobWrapper
func main() 自定义日志
package main
import (
"log"
"os"
"github.com/robfig/cron/v3"
)
// 实现 cron.Logger 接口
type CronLogger struct
func (msg string, keysAndValues ...interface)
func (err error, msg string, keysAndValues ...interface)
func main() 任务管理与控制
任务的生命周期
┌─────────────────────────────────────────────────────────────────┐
│ 任务生命周期 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 创建 │ ──► │ 就绪 │ ──► │ 运行 │ │
│ │ AddFunc │ │ 等待触发 │ │ 执行任务 │ │
│ └──────────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │
│ ┌────────────────┼────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ │
│ │ 移除 │ │ 暂停 │ │
│ │ Remove │ │ Stop │ │
│ └──────────┘ └──────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘启动与停止
package main
import (
"context"
"log"
"os"
"os/signal"
"syscall"
"github.com/robfig/cron/v3"
)
func main() 动态添加和删除任务
package main
import (
"fmt"
"log"
"time"
"github.com/robfig/cron/v3"
)
func main() 获取任务信息
package main
import (
"fmt"
"log"
"time"
"github.com/robfig/cron/v3"
)
func main() 任务状态监控
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"sync"
"time"
"github.com/robfig/cron/v3"
)
// 任务状态信息
type JobStatus struct
// 任务管理器
type JobManager struct
func NewJobManager() *JobManager
func (name, expr string, fn func() error) (cron.EntryID, error)
func () []JobStatus
func ()
func () context.Context
// HTTP 接口
func (w http.ResponseWriter, r *http.Request)
func main() 错误处理与日志
全局错误处理
package main
import (
"log"
"runtime/debug"
"github.com/robfig/cron/v3"
)
// 全局错误处理中间件
func ErrorHandlerWrapper(onError func(jobName string, err interface)) cron.JobWrapper
// 错误通知函数
func notifyError(jobName string, err interface)
func main() 结构化日志
package main
import (
"os"
"time"
"github.com/robfig/cron/v3"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
// 使用 zerolog 的 Cron Logger
type ZerologCronLogger struct
func (msg string, keysAndValues ...interface)
func (err error, msg string, keysAndValues ...interface)
func main() 任务执行日志记录
package main
import (
"log"
"time"
"github.com/robfig/cron/v3"
"gorm.io/gorm"
)
// 任务执行记录
type JobExecutionLog struct
// 日志记录中间件
func LoggingWrapper(db *gorm.DB, jobName string) cron.JobWrapper 分布式定时任务
在分布式环境中,需要确保定时任务不会被多个实例重复执行。
基于 Redis 的分布式锁
package main
import (
"context"
"log"
"time"
"github.com/go-redis/redis/v8"
"github.com/robfig/cron/v3"
)
type DistributedLock struct
func NewDistributedLock(client *redis.Client, key string, ttl time.Duration) *DistributedLock
func (ctx context.Context) (bool, error)
func (ctx context.Context) error
// 分布式任务中间件
func DistributedWrapper(client *redis.Client, lockTTL time.Duration) cron.JobWrapper
func main() 使用 Redsync 库
package main
import (
"log"
"time"
"github.com/go-redis/redis/v8"
"github.com/go-redsync/redsync/v4"
"github.com/go-redsync/redsync/v4/redis/goredis/v8"
"github.com/robfig/cron/v3"
)
func main() 分布式调度架构
┌─────────────────────────────────────────────────────────────────┐
│ 分布式定时任务架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 服务实例 │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ 实例 A │ │ 实例 B │ │ 实例 C │ │ │
│ │ │ cron │ │ cron │ │ cron │ │ │
│ │ └────┬────┘ └────┬────┘ └────┬────┘ │ │
│ │ │ │ │ │ │
│ └───────┼──────────────┼──────────────┼────────────────────┘ │
│ │ │ │ │
│ └──────────────┼──────────────┘ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Redis (分布式锁) │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ cron_lock:data_sync = "instance_a_uuid" TTL=60s │ │ │
│ │ │ cron_lock:cleanup = "instance_b_uuid" TTL=120s │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ 执行流程: │
│ 1. 所有实例同时触发定时器 │
│ 2. 尝试获取 Redis 分布式锁 │
│ 3. 只有一个实例获取成功并执行 │
│ 4. 其他实例跳过本次执行 │
│ │
└─────────────────────────────────────────────────────────────────┘实战案例
案例1:数据备份任务
package main
import (
"compress/gzip"
"fmt"
"io"
"log"
"os"
"os/exec"
"path/filepath"
"time"
"github.com/robfig/cron/v3"
)
type BackupConfig struct
type DatabaseBackupJob struct
func ()
func ()
func main() 案例2:订单超时处理
package main
import (
"context"
"log"
"time"
"github.com/robfig/cron/v3"
"gorm.io/gorm"
)
type Order struct
type OrderTimeoutJob struct
func ()
func main() 案例3:缓存刷新任务
package main
import (
"context"
"encoding/json"
"log"
"time"
"github.com/go-redis/redis/v8"
"github.com/robfig/cron/v3"
"gorm.io/gorm"
)
type Product struct
type CacheRefreshJob struct
func ()
func (ctx context.Context)
func (ctx context.Context)
func (ctx context.Context)
func main() 案例4:完整的任务调度服务
package main
import (
"context"
"encoding/json"
"log"
"net/http"
"os"
"os/signal"
"sync"
"syscall"
"time"
"github.com/robfig/cron/v3"
)
// 任务定义
type JobDefinition struct
// 调度器服务
type SchedulerService struct
func NewSchedulerService() *SchedulerService
// 注册内置任务
func ()
func (name, expr string, fn func()) error
func (name string)
// HTTP API
func ()
func ()
func ()
func main() 最佳实践与注意事项
任务设计原则
┌─────────────────────────────────────────────────────────────────┐
│ 定时任务设计原则 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1️⃣ 幂等性 │
│ • 任务重复执行应产生相同结果 │
│ • 使用唯一约束防止重复数据 │
│ • 记录处理进度,支持断点续传 │
│ │
│ 2️⃣ 原子性 │
│ • 使用事务保证数据一致性 │
│ • 失败时能够回滚或重试 │
│ │
│ 3️⃣ 可观测性 │
│ • 记录详细的执行日志 │
│ • 监控任务执行时长和成功率 │
│ • 配置告警通知 │
│ │
│ 4️⃣ 容错性 │
│ • 捕获并处理异常 │
│ • 实现重试机制 │
│ • 设置超时限制 │
│ │
│ 5️⃣ 可控性 │
│ • 支持手动触发和停止 │
│ • 提供任务状态查询接口 │
│ • 支持动态调整执行时间 │
│ │
└─────────────────────────────────────────────────────────────────┘常见陷阱与解决方案
| 陷阱 | 问题描述 | 解决方案 |
|---|---|---|
| 任务堆积 | 任务执行时间超过调度间隔 | 使用 SkipIfStillRunning 或 DelayIfStillRunning |
| 时区混乱 | 服务器时区与业务时区不一致 | 明确指定 WithLocation |
| 内存泄漏 | 任务中的资源未正确释放 | 使用 defer 释放资源,定期检查内存 |
| Panic 导致崩溃 | 未捕获的异常导致整个调度器停止 | 使用 Recover 中间件 |
| 分布式重复执行 | 多个实例同时执行同一任务 | 使用分布式锁 |
| 长时间任务阻塞 | 某个任务执行过久占用资源 | 设置超时控制 |
性能优化建议
// 1. 批量处理数据
func ()
// 2. 使用 goroutine 池
func ()
// 3. 避免在任务中进行大量内存分配
func () 监控指标
建议监控以下指标:
| 指标 | 说明 | 告警阈值建议 |
|---|---|---|
cron_job_duration_seconds | 任务执行时长 | P99 > 预期时间的 2 倍 |
cron_job_success_total | 成功次数 | - |
cron_job_failure_total | 失败次数 | 连续 3 次失败 |
cron_job_last_success_time | 上次成功时间 | 超过预期间隔的 2 倍未执行 |
cron_job_running | 当前运行中的任务数 | 超过总任务数 |
// Prometheus 指标示例
var (
jobDuration = prometheus.NewHistogramVec(
prometheus.HistogramOpts,
[]string,
)
jobSuccess = prometheus.NewCounterVec(
prometheus.CounterOpts,
[]string,
)
jobFailure = prometheus.NewCounterVec(
prometheus.CounterOpts,
[]string,
)
)
func MetricsWrapper(jobName string) cron.JobWrapper 生产环境检查清单
┌─────────────────────────────────────────────────────────────────┐
│ 生产环境上线检查清单 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ □ Cron 表达式已验证(使用在线工具测试) │
│ □ 时区配置正确 │
│ □ 使用了 Recover 中间件 │
│ □ 配置了适当的日志记录 │
│ □ 实现了错误告警机制 │
│ □ 分布式环境使用了分布式锁 │
│ □ 任务具有幂等性 │
│ □ 设置了合理的超时时间 │
│ □ 监控指标已接入 │
│ □ 优雅关闭逻辑已实现 │
│ □ 任务执行记录可追溯 │
│ □ 手动触发接口可用(用于紧急情况) │
│ │
└─────────────────────────────────────────────────────────────────┘配置文件示例
# config/cron.yaml
scheduler:
timezone: "Asia/Shanghai"
enable_seconds: true
jobs:
- name: "data-backup"
expression: "0 2 * * *"
enabled: true
timeout: 3600s
retry:
max_attempts: 3
delay: 60s
- name: "order-timeout"
expression: "* * * * *"
enabled: true
timeout: 300s
distributed_lock:
enabled: true
ttl: 120s
- name: "cache-refresh"
expression: "*/5 * * * *"
enabled: true
timeout: 60s
- name: "daily-report"
expression: "0 8 * * *"
enabled: true
timeout: 1800s
notify:
on_success: false
on_failure: true
channels: 参考资料
官方文档
- robfig/cron GitHub - 官方仓库
- robfig/cron GoDoc - API 文档
Cron 表达式工具
- Crontab Guru - 在线 Cron 表达式解析器
- Cron Expression Generator - 表达式生成器
相关库
- go-co-op/gocron - 另一个流行的 Go 定时任务库
- go-redsync/redsync - Redis 分布式锁
- hibiken/asynq - 基于 Redis 的任务队列
请先登录后再发表评论