内容简介:本文基于在开发中,经常遇到一些需要定时任务的场景。各个语言都有定时语言的库,
本文基于 Golang Crontab 实现了一个Crontab Job Manager。更加容易使用,同时也能够满足更加复杂的场景。
仓储地址 , 如果有用,欢迎点赞,欢迎讨论,欢迎找茬。
需求
在开发中,经常遇到一些需要定时任务的场景。各个语言都有定时语言的库, Golang Cron 提供了Crontab Golang语言版本。这个库非常不错,提供最基本的定时任务编排的功能。但是一些复杂需求无法满足,比如
- 任何定时任务都有可能失败,失败了就panic了,这样非常不友好。最起码能够让我控制,失败是重试还是停止
- 某些任务执行周期要10s, 而用户设置的5s一执行,我能不能保证任何时间这个任务只执行一次
- 我想实时的看到任务的状态,比如是不是在运行?下次运行时间?上次运行时间?
- 我想看到任务执行了多少次,成功了多少次
- 我想要限制最大任务数量,比如超过10个任务在执行,不运行新的任务执行
- 任务执行完了可以告诉我逻辑上有错误,还是有结果。我还可以加上一些钩子函数来处理任务执行的结果
以上的需求都非常常见,可惜这个库都不支持 ^_^.
完全没用的例子
复杂定义任务的场景模型抽象出来大概也就是下面几个功能点,这个没用的例子可以很好的体现出来
- 用户通过接口,告诉后台我要做一个什么定时工作,schedule是什么
- 查看所有定时任务的状态
- 查看所有定时任务的工作结果
本地运行
通过以下命令本地运行
go get -u "github.com/OhBonsai/croner" go get -u "github.com/gin-gonic/gin" go get -u "github.com/gorilla/websocket" cd $GOPATH/src/github.com/OhBonsai/croner/example go run server.go # 打开localhost:8000
完全没用的例子
前端解释
原谅我的狗屎前端。怕大家看不懂,我还是解释一下前端各个部分什么意思。
- 图中①的区域,是计划定义区,可以设置一些参数,表示
谁多久往聊天室说一句什么话
。第二个表单可以输入1-10
的数字,表示每隔几秒说话。当然cron
支持六位的crontab周期定义。 - 图中②的区域,是执行任务状态区,每秒刷新一次
- 图中3的区域,就是我们的聊天室啦。后台定时任务钩子函数会定时把消息推到
channel
中,如果websocket服务端收到消息就发送到浏览器
后端逻辑
- 实现定时计划接口
func Run() croner.JobRunReturn
type JobS struct { Duration int `json:"duration"` Who string `json:"who"` What string `json:"what"` } func (j JobS) Run() croner.JobRunReturn { return croner.JobRunReturn{ Value: fmt.Sprintf("[%s] %s: %s", time.Now().Format(time.RFC850), j.Who, j.What), } }
- 初始化设置
var manager = croner.NewCronManager(croner.CronManagerConfig{ true, false, 0, 0, })
- 加上钩子函数,如果接收到任务执行结果,将结果传到
ch
channel
croner.OnJobReturn(func(runReturn *croner.JobRunReturnWithEid) { say := runReturn.Value.(string) ch <- say })
- 每当接受到post请求,就创建一个任务
_, err = manager.Add(fmt.Sprintf("@every %ds", curJob.Duration), curJob, nil)
- 轮询获区
ch
传过来的值,通过websocket传到前端
for { select { case msg := <-ch: conn.WriteMessage(websocket.TextMessage, []byte(msg)) default: continue } }
实现
详细的使用可以查看 测试文件 ,
任务接口
任务只要实现 run()
函数就行啦。这样我就可以包装你这个函数
type JobRunReturn struct { Value interface{} Error error } type JobInf interface { Run() JobRunReturn }
任务失败控制
Cron
没有失败控制,通过包装 run()
函数来实现 cron
的job接口来增加一些逻辑。加上一个 defer
来恢复 panic
, 通过设置配置 ignorePanic
来控制是否忽略错误继续执行,还是发生错误就是 STOP
defer func() { j.TotalCount += 1 if err := recover(); err != nil { errString := fmt.Sprintf("WrappedJob-%d %s execute fail. error is %s", j.Id, j.Name, err) println(errString) atomic.StoreUint32(&j.status, FAIL) if !j.father.ignorePanic { j.father.DisActive(j.Id) } j.father.jobReturnsWithEid <- JobRunReturnWithEid{ JobRunReturn{nil, JobRunError{errString}}, j.Id, } } return }()
单任务周期时间只执行一次
这个主要靠锁来实现,任务运行时就锁住,直到完成之后才释放
j.running.Lock() defer j.running.Unlock()
任务状态变更
通过原子操作来变更任务状态
atomic.StoreUint32(&(j.status), RUNNING) defer atomic.StoreUint32(&(j.status), IDLE)
最大任务数量
通过 buffered channel
来实现最大任务数量
permit = make(chan struct{}, c.PoolSize) permit <- struct{}{} defer func() { <-permit }()
钩子
不断获取任务回传结果,然后遍历执行钩子函数
go func(){ for { select { case value := <-r.jobReturnsWithEid: jobReturnHooks.Run(&value) case <-r.stop: return } } }()
缺陷
超时停止,本来尝试做的,配置里面都预留了这个字段。结果发现有问题。这个貌似要修改croner的源码,我不想这么做,但又想不出其他实现方案,我毕竟刚使用golang编程。如果有读者碰到类似问题或者有想法留言提醒我呀
OnlyOne单次执行的时候,下次执行的时间就无法预测了。这个时候把任务的 Next
设置为一个不可能的值,比如1970-0-0。但如果在周期内执行完了,下次执行时间就准了...这貌似没办法解决。我也不知道任务什么时候执行完。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 如何井井有条的管理开发任务(附任务管理模板)
- 任务管理工具 jiacrontabv 1.4.5 更新
- 选择scrum任务看板管理工具的要点
- guns-lite 新增定时任务管理功能
- 开源golang兼容crontab的定时任务管理器
- 若依管理系统 1.0.8 发布,新增定时任务
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Lean Startup
Eric Ries / Crown Business / 2011-9-13 / USD 26.00
更多中文介绍:http://huing.com Most startups fail. But many of those failures are preventable. The Lean Startup is a new approach being adopted across the globe, chan ging the way companies are built and ......一起来看看 《The Lean Startup》 这本书的介绍吧!