# golang 定时任务 {#golang-定时任务}
本文讲述 golang 中如何管理定时任务。原生的定时任务功能不好用也不强大,这里推荐 github.com/rfyiamcool/cronlib,该库支持动态添加、修改、删除定时任务,最具特色的功能是可以为每个定时任务指定唯一的标识 ID,该功能很实用,然而在当前最火的定时任务库 github.com/robfig/cron 上却没有(实际上 cronlib 就是基于 robfig 开发的)。所以本文强烈推荐 github.com/rfyiamcool/cronlib。
# 1. 封装 1 个通用的定时任务处理文件 {#_1-封装-1-个通用的定时任务处理文件}
为了方便调用,创建一个 cron_task.go 文件,里面封装了通用的定时任务处理逻辑。
package cron
import (
"apiproject/config"
"apiproject/log"
"errors"
"github.com/rfyiamcool/cronlib"
"go.uber.org/zap"
)
type CallbackTask func(paraMap *map[string]interface{})
var MyCronSchduler *cronlib.CronSchduler
/**
启动定时任务列表
*/
func Init() {
//任务开关是否开启
if !config.GlobalConfig.TaskSwitchOn {
log.Logger.Info("启动定时任务列表, 任务开关没有开启")
return
}
MyCronSchduler = cronlib.New()
MyCronSchduler.Start()
}
/**
注册定时任务
*/
func RegisterTask(jobName string, jobSpec string,
callbackTask CallbackTask, callbackParaMap *map[string]interface{}) error {
//检测定时任务是否已存在
if IsExistTask(jobName) {
log.Logger.Error("注册定时任务, 定时任务已存在", zap.Any("jobName", jobName))
return errors.New("定时任务已存在")
}
//创建定时任务
job, err := cronlib.NewJobModel(
jobSpec,
func() {
log.Logger.Info("定时任务开始执行", zap.Any("jobName", jobName), zap.Any("jobSpec", jobSpec))
callbackTask(callbackParaMap)
log.Logger.Info("定时任务执行完成", zap.Any("jobName", jobName), zap.Any("jobSpec", jobSpec))
},
)
if err != nil {
return err
}
//注册定时任务
err = MyCronSchduler.DynamicRegister(jobName, job)
if err != nil {
return err
}
log.Logger.Info("注册定时任务", zap.String("jobName", jobName), zap.String("jobSpec", jobSpec))
return nil
}
/**
更新定时任务
*/
func UpdateTask(jobName string, jobSpec string,
callbackTask CallbackTask, callbackParaMap *map[string]interface{}) error {
//创建定时任务
job, err := cronlib.NewJobModel(
jobSpec,
func() {
log.Logger.Info("定时任务开始执行", zap.Any("jobName", jobName), zap.Any("jobSpec", jobSpec))
callbackTask(callbackParaMap)
log.Logger.Info("定时任务执行完成", zap.Any("jobName", jobName), zap.Any("jobSpec", jobSpec))
},
)
if err != nil {
return err
}
//更新定时任务
err = MyCronSchduler.UpdateJobModel(jobName, job)
if err != nil {
return err
}
log.Logger.Info("更新定时任务", zap.String("jobName", jobName), zap.String("jobSpec", jobSpec))
return nil
}
/**
反注册定时任务
*/
func UnRegisterTask(jobName string) error {
if err := MyCronSchduler.UnRegister(jobName); err != nil {
log.Logger.Error("反注册定时任务, 失败", zap.Any("jobName", jobName), zap.Error(err))
return err
}
return nil
}
/**
是否存在某定时任务
*/
func IsExistTask(jobName string) bool {
_, err := MyCronSchduler.GetServiceCron(jobName)
log.Logger.Info("是否存在某定时任务", zap.Any("jobName", jobName), zap.Any("exist", err == nil))
return err == nil
}
说明:
- Init函数 需要在应用启动时手动调用该函数,完成全局定时任务管理器的初始化。
- 定时任务的属性
可以指定定时任务的 ID、定时表达式、回调函数、传给回调函数的参数。
# 2. 初始化 {#_2-初始化}
系统启动时, 调用前文的 Init 方法, 完成全局定时任务管理器的初始化。
# 3. 创建定时任务 {#_3-创建定时任务}
此处提供 2 个例子。
# 3.1 例 1 {#_3-1-例-1}
err := cron.RegisterTask("爬取新闻数据", config.GlobalConfig.TaskCrawnewslistCron, s_spider.SpiderService.CrawNewsListTask, nil)
if err != nil {
panic(err)
}
# 3.2 例 2 {#_3-2-例-2}
/**
注册重访上线定时任务
*/
func (this *revisitService) registerRevisitOnlineTask(distributor *m_distributor.Distributor) error {
jobName := this.getRevisitOnlineJobName(distributor.ID)
paraMap := map[string]interface{}{}
paraMap["did"] = distributor.ID
//反注册
if cron.IsExistTask(this.getRevisitOnlineJobName(distributor.ID)) {
if err := cron.UnRegisterTask(this.getRevisitOnlineJobName(distributor.ID)); err != nil {
log.Logger.Error("注册重访上线定时任务, 反注册失败", zap.Any("did", distributor.ID), zap.Error(err))
return errors.New("反注册失败")
}
log.Logger.Info("注册重访上线定时任务, 当前任务已存在", zap.Any("did", distributor.ID))
}
//注册
if distributor.RevisitOnlineTime != "" {
if err := cron.RegisterTask(jobName, distributor.RevisitOnlineTime, this.RevisitOnlineTaskCallback, ¶Map); err != nil {
log.Logger.Error("注册重访上线定时任务, 失败", zap.Any("jobName", jobName), zap.Any("did", distributor.ID), zap.Error(err))
return err
}
log.Logger.Info("注册重访上线定时任务, 完成", zap.Any("distributor", distributor))
}
return nil
}
# 4 参考资料 {#_4-参考资料}
详细用法详见cronlib 的官网 (opens new window)
cronlib 是基于robfig (opens new window)开发而来。robfig 是最火的定时任务库,但是我觉得封装之后的 cronlib 变得更好用。