golang封装可扩展的crontab

1. 导入cron第三方包

Go 复制代码
go get github.com/robfig/cron/v3

2. 申明变量

Go 复制代码
// 单个cron的interface
type RunCronItem interface {
	Run()         // 计划任务执行体
	Start() error // 初始化用的,第一次启动的时候,调用,后续不再执行
	Name() string // 计划任务配置名
}

// 申明所有计划任务的配置
type CronTaskItem struct {
	name    string
	runItem RunCronItem
}

// JobStatus 任务状态
type JobStatus struct {
	LastRun     time.Time
	LastSuccess bool
	IsRunning   bool
	Mutex       sync.Mutex
}

// CronManager Cron任务管理器
type CronManager struct {
	cron      *cron.Cron
	jobs      map[string]func()
	jobStatus map[string]*JobStatus
	config    config.ProxyConfig // 此处为配置文件
}

3. 配置文件

config.yaml

Go 复制代码
cron_jobs:
  start_file_resend:
    Schedule: "0 */2 * * * *"  # 每2分钟将trace文件发送到客户端
    Enabled: false
    Description: "每2分钟将文件发到云端"

config.go

Go 复制代码
import (
	"flag"
	"github.com/spf13/viper"
	"log"
)

type ProxyConfig struct {
...
	LogLevel     uint32                   `mapstructure:"log_level"`
	Log          LogConfig                `mapstructure:"log"`
	CronJobs     map[string]CronJobConfig `mapstructure:"cron_jobs"` //此处为计划任务的配置
....

}

4. 创建计划任务管理器

Go 复制代码
// NewCronManager 创建Cron管理器
func NewCronManager(appConfig config.ProxyConfig) *CronManager {
	// 创建Cron实例,使用秒级精度(支持6位cron表达式)
	c := cron.New(cron.WithSeconds())

	manager := &CronManager{
		cron:      c,
		jobs:      make(map[string]func()),
		jobStatus: make(map[string]*JobStatus),
		config:    appConfig,
	}

	return manager
}

// AddJob 添加任务
func (m *CronManager) AddJob(name string, jobItem RunCronItem) error {
	// 查找配置
	jobConfig, exists := m.config.CronJobs[name]
	if !exists {
		return fmt.Errorf("定时任务名称找不到:%s", name)
	}

	if !jobConfig.Enabled {
		logger.Log.Error("定时任务未启用", zap.String("name", name))
		return nil
	}

	// 初始化任务状态
	m.jobStatus[name] = &JobStatus{}

	// 包装任务函数,添加并发控制
	wrappedFunc := func() {
		status := m.jobStatus[name]

		// 检查任务是否正在运行
		status.Mutex.Lock()
		if status.IsRunning {
			logger.Log.Info("定时任务已经运行中,请稍后", zap.String("name", name))
			status.Mutex.Unlock()
			return
		}

		// 标记任务开始运行
		status.IsRunning = true
		status.LastRun = time.Now()
		status.Mutex.Unlock()

		// 执行任务
		logger.Log.Debug("定时任务正在执行", zap.String("name", name))
		startTime := time.Now()

		defer func() {
			// 捕获panic,确保任务状态正确更新
			if r := recover(); r != nil {
				logger.Log.Error(fmt.Sprintf("定时任务 %s panicked: %v", name, r))
				status.LastSuccess = false
			}

			// 更新任务状态
			status.Mutex.Lock()
			status.IsRunning = false
			status.Mutex.Unlock()

			duration := time.Since(startTime)
			logger.Log.Debug(fmt.Sprintf("定时任务完成:%s, duration: %v", name, duration))
		}()

		// 执行实际任务
		jobItem.Run()
		status.LastSuccess = true
	}

	// 添加任务到cron
	m.jobs[name] = wrappedFunc
	_, err := m.cron.AddFunc(jobConfig.Schedule, wrappedFunc)
	if err != nil {
		return fmt.Errorf("failed to add job %s: %v", name, err)
	}

	logger.Log.Debug(fmt.Sprintf("添加定时任务成功: %s with schedule: %s", name, jobConfig.Schedule))

	return nil
}

// StartAllJobs 启动所有配置中启用的任务
func (m *CronManager) StartAllJobs() error {
	for name := range m.config.CronJobs {
		// 这里不直接添加任务,因为任务函数需要外部提供
		// 外部需要先调用 AddJob 添加具体的任务函数
		logger.Log.Debug(fmt.Sprintf("启用所有配置中的定时任务: %s", name))
	}

	m.cron.Start()
	logger.Log.Debug("定时任务已全部开启")
	return nil
}

// Start 启动所有任务
func (m *CronManager) Start() {
	m.cron.Start()
	logger.Log.Debug("Cron manager started")
}

// Stop 停止所有任务
func (m *CronManager) Stop() {
	m.cron.Stop()
	logger.Log.Debug("Cron manager stopped")
}

// GetJobStatus 获取任务状态
func (m *CronManager) GetJobStatus(name string) (*JobStatus, error) {
	status, exists := m.jobStatus[name]
	if !exists {
		return nil, fmt.Errorf("job %s not found", name)
	}
	return status, nil
}

// ListJobs 列出所有任务
func (m *CronManager) ListJobs() []string {
	var jobs []string
	for name := range m.jobs {
		jobs = append(jobs, name)
	}
	return jobs
}

5. 配置cron和启动

Go 复制代码
// 此处配置您的计划任务列表
func crontMangerList() []CronTaskItem {
	cronTaskList := []RunCronItem{
		... // 这里可以配置你自己的计划任务的struct,返回满足interface RunCronItem

	}
	crontaskItems := make([]CronTaskItem, 0, len(cronTaskList))
	for _, item := range cronTaskList {
		crontaskItems = append(crontaskItems, CronTaskItem{
			name:    item.Name(),
			runItem: item,
		})
	}
	return crontaskItems
}

// 启动job客户端
func InitCronJobClient(config config.ProxyConfig) {
	manager := NewCronManager(config)
	// 添加任务,使用 WaitGroup 跟踪
	cronList := crontMangerList()
	for _, cronItem := range cronList {
		err := manager.AddJob(cronItem.name, cronItem.runItem)
		if err != nil {
			logger.Log.Error("加入定时任务失败job:%s err:%v", zap.String("job_name", cronItem.name), zap.Error(err))
		}
		err = cronItem.runItem.Start()
		if err != nil {
			panic(fmt.Sprintf("计划任务初始化失败:%v", err))
		}
	}

	// 启动 Cron 管理器
	manager.Start()
	logger.Log.Info("CronJob InitCronJobClient")
}
相关推荐
Lxinccode6 分钟前
python(59) : 多线程调用大模型ocr提取图片文本
开发语言·python·图片提取文字·批量提取文件·多线程ocr
自由日记14 分钟前
python简单线性回归
开发语言·python·线性回归
程序员-周李斌15 分钟前
Java NIO [非阻塞 + 多路复用解]
java·开发语言·开源软件·nio
猪八戒1.016 分钟前
onenet接口
开发语言·前端·javascript·嵌入式硬件
程序猿小蒜18 分钟前
基于Spring Boot的宠物领养系统的设计与实现
java·前端·spring boot·后端·spring·宠物
h***839325 分钟前
JavaScript开源
开发语言·javascript·ecmascript
Z***258038 分钟前
JavaScript虚拟现实案例
开发语言·javascript·vr
k***82511 小时前
springboot整合libreoffice(两种方式,使用本地和远程的libreoffice);docker中同时部署应用和libreoffice
spring boot·后端·docker
tan180°1 小时前
Linux网络TCP(上)(11)
linux·网络·c++·后端·tcp/ip
l***46681 小时前
springboot使用redis
spring boot·redis·后端