本文基于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. 图中①的区域,是计划定义区,可以设置一些参数,表示谁多久往聊天室说一句什么话。第二个表单可以输入1-10的数字,表示每隔几秒说话。当然cron支持六位的crontab周期定义。
  2. 图中②的区域,是执行任务状态区,每秒刷新一次
  3. 图中3的区域,就是我们的聊天室啦。后台定时任务钩子函数会定时把消息推到channel中,如果websocket服务端收到消息就发送到浏览器

后端逻辑

  1. 实现定时计划接口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),
}
}
  1. 初始化设置
var manager = croner.NewCronManager(croner.CronManagerConfig{
true, false, 0, 0,
})
  1. 加上钩子函数,如果接收到任务执行结果,将结果传到ch channel
croner.OnJobReturn(func(runReturn *croner.JobRunReturnWithEid) {
say := runReturn.Value.(string)
ch <- say
})
  1. 每当接受到post请求,就创建一个任务
_, err = manager.Add(fmt.Sprintf("@every %ds", curJob.Duration), curJob, nil)
  1. 轮询获区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。但如果在周期内执行完了,下次执行时间就准了...这貌似没办法解决。我也不知道任务什么时候执行完。

学习强大的APScheduler, Quartz

Go 定时任务的更多相关文章

  1. Java定时任务的常用实现

    Java的定时任务有以下几种常用的实现方式: 1)Timer 2)ScheduledThreadPoolExecutor 3)Spring中集成Cron Quartz 接下来依次介绍这几类具体实现的方 ...

  2. [转]Java实现定时任务的三种方法

    在应用里经常都有用到在后台跑定时任务的需求.举个例子,比如需要在服务后台跑一个定时任务来进行非实时计算,清除临时数据.文件等.在本文里,我会给大家介绍3种不同的实现方法: 普通thread实现 Tim ...

  3. 使用python crontab设置linux定时任务

    熟悉linux的朋友应该知道在linux中可以使用crontab设置定时任务.可以通过命令crontab -e编写任务.当然也可以直接写配置文件设置任务. 但是有时候希望通过脚本自动设置,比如我们应用 ...

  4. C#定时任务组件之FluentScheduler

    FluentScheduler是.NET开源处理定时任务组件 1.任务的创建注册 public static void TaskActionByMinutes(Action action, int c ...

  5. 浅谈 linux 例行性工作 crontab (linux定时任务)

    定时任务大家都挺说过,就好比你手机上的闹钟,到了指定的时候就会响起. 今天在对redis缓存进行定时储存时又操作了一把,发现一些细节,写的不好.大家就将就看吧, 首先 简单介绍一下linux 例行性工 ...

  6. SpringMVC中定时任务配置

    在项目中使用定时任务是常有的事,比如每天定时进行数据同步或者备份等等. 以前在从事C语言开发的时候,定时任务都是通过写个shell脚本,然后添加到linux定时任务中进行调度的. 现在使用Spring ...

  7. springboot(九):定时任务

    在我们的项目开发过程中,经常需要定时任务来帮助我们来做一些内容,springboot默认已经帮我们实行了,只需要添加相应的注解就可以实现 1.pom包配置 pom包里面只需要引入springboot ...

  8. 详解java定时任务

    在我们编程过程中如果需要执行一些简单的定时任务,无须做复杂的控制,我们可以考虑使用JDK中的Timer定时任务来实现.下面LZ就其原理.实例以及Timer缺陷三个方面来解析JavaTimer定时器. ...

  9. [JAVA]定时任务之-Quartz使用篇

    Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用.Quartz可以用来创建简单或为运行十个,百个, ...

  10. 【译】Spring 4 基于TaskScheduler实现定时任务(注解)

    前言 译文链接:http://websystique.com/spring/spring-job-scheduling-with-scheduled-enablescheduling-annotati ...

随机推荐

  1. Vscode新建html文件

    在Vscode新建html文件 1.点击Open Folder: 2.选择目标文件夹,新建一个拓展名为html的文件: 3.在第1行输入!(英文状态下),按tab键,新建成功.界面如下图所示:

  2. Angular(02)-- Angular-CLI命令

    声明 本系列文章内容梳理自以下来源: Angular 官方中文版教程 官方的教程,其实已经很详细且易懂,这里再次梳理的目的在于复习和巩固相关知识点,刚开始接触学习 Angular 的还是建议以官网为主 ...

  3. RadioButton监听事件

    RadioButton为单选按钮,他需要与RadioGroup配合使用 对应的布局代码: <?xml version="1.0" encoding="utf-8&q ...

  4. ionic cordova build android error: commamd failed with exit code eacces

    问题: 电脑的gradle版本为Gradle 5.0,然而 因为 添加的android 平台为6.3.0 gradle 是 4.1版本 电脑已存在 gradle的情况下,add platform 成功 ...

  5. express+handlebars 快速搭建网站前后台

    最近在重构公司网站,原来网站使用PHP,前后端不分离,添加与更新网站内容仍使用原始方法,先出布局再把调好的布局给PHP后端开发,花时间长,维护不易.因此决定将网站前后端分离,核心功能含网站下单及CRM ...

  6. Spring boot 配置文件详解 (properties 和yml )

    从其他框架来看 我们都有自己的配置文件, hibernate有hbm,mybatis 有properties, 同样, Spring boot 也有全局配置文件. Springboot使用一个全局的配 ...

  7. ==运算符和equals()方法的区别

    Java语言程序中判断两个变量是否相等有两种方式:一是运用==运算符,二是运用equals方法. 1. ==运算符 对于==运算符来说,如果两个变量是基本类型的,并且是数值类型,则只要它们的值相等,就 ...

  8. [Inside HotSpot] C1编译器优化:条件表达式消除

    1. 条件传送指令 日常编程中有很多根据某个条件对变量赋不同值这样的模式,比如: int cmov(int num) { int result = 10; if(num<10){ result ...

  9. ubuntu-18.04 设置开机启动脚本-亲测有效

    ubuntu-18.04不能像ubuntu14一样通过编辑rc.local来设置开机启动脚本,通过下列简单设置后,可以使rc.local重新发挥作用. 2.将下列内容复制进rc-local.servi ...

  10. Android6.0运行时权限(基于RxPermission开源库)

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 在6.0以前的系统,都是权限一刀切的处理方式,只要用户安装,Manifest申请的权限都会被赋予,并且安装后权限也撤销不了. And ...