本文基于Golang Crontab 实现了一个Crontab Job Manager。更加容易使用,同时也能够满足更加复杂的场景。
仓储地址, 如果有用,欢迎点赞,欢迎讨论,欢迎找茬。
在开发中,经常遇到一些需要定时任务的场景。各个语言都有定时语言的库,Golang Cron 提供了Crontab Golang语言版本。这个库非常不错,提供最基本的定时任务编排的功能。但是一些复杂需求无法满足,比如
以上的需求都非常常见,可惜这个库都不支持^_^.
复杂定义任务的场景模型抽象出来大概也就是下面几个功能点,这个没用的例子可以很好的体现出来
通过以下命令本地运行
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周期定义。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
channelcroner.OnJobReturn(func(runReturn *croner.JobRunReturnWithEid) {
say := runReturn.Value.(string)
ch <- say
})
_, 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。但如果在周期内执行完了,下次执行时间就准了...这貌似没办法解决。我也不知道任务什么时候执行完。