作为程序员,在日常工作中免不了与各种任务打交道,有一些任务的执行是具有重复性质的,比如工作人员每天登录系统之后进行一系列基础操作:(1)先确认一下系统时间,(2)查看之前登录的用户,(3)再打开某个监控日志……这些操作基本上是固定项目,每次一登录就需要一个一个地执行,那么,如何节省工程师宝贵的时间,把这些操作捆绑起来,只通过一次输入就全部执行呢?在 Unix/Linux 环境下,shell 脚本出现了,它负责处理批量任务,类似于 Windows 环境下的批处理 .bat 文件,都是按照先后顺序,依次执行文件中的每一个任务条目。
而再进一步,工程师又想让任务能够定时自动执行,不必等到每次想让任务执行时再操作,大名鼎鼎的 crontab 工具就满足了他们的愿望。crontab 是 Linux 下的一个与 Windows 下的计划任务类似的命令,它被用来管理用户需要周期性执行的任务,由守护进程 crond 驱动,crond 定期检查是否有任务需要执行,如果有那么就执行相应任务。更具体一点,通过 crontab 命令参数与相应格式的设置,它能够做到对定时任务进行按需管理。
然而,还是有追求极致体验与性能的开发者不够满意 crontab,他们非要自己改造/创造一个新的替代工具,以适应自己的工作方式。本次采访嘉宾周君就开发了一个号称 crontab 替代品的 JTimer,我们请他分享了 JTimer 的开发背景与相关技术细节。
定时任务具体是指什么?定时任务管理是什么?介绍一下整个工作场景。
定时任务是指在用户指定的时间点执行用户指定的任务。而随着业务的增加与变动,定时任务常常需要修改,定时任务的管理就是对定时任务的 CRUD 等操作。
大多数业务系统都会有使用定时任务的需求,以此来代替人工的驻守与繁琐的重复操作,例如某系统有每天生成业务报表的需求,但是由于数据量过于庞大,为了避免对生产环境用户造成影响,应选择在凌晨访问量较少时执行生成报表的操作,定时任务不仅节省了人力,免去了人工可能导致的“忘了执行任务”的尴尬场面,也能让开发人员不用半夜起床工作。
一个定时任务管理器的优劣主要可以从哪些方面考量?
通常,我们对定时任务管理器的要求最主要的考量有以下两方面:
- 定时器的稳定性、高可用性
- 管理上的便捷性
第一点考量是最为重要的,定时任务是按时执行用户指定的业务任务,定时器的不稳定、延迟与宕机等问题都可能直接影响用户的业务,严重的甚至导致公司的经济损失。
至于上述的第二点考量,对运维和开发人员来说是一个痛点,如果管理不够便捷,那么也会严重影响到工程师的效率。
JTimer 号称 crontab 的替代品,那么与之相比,JTimer 有什么优势?二者还有哪些异同?
crontab 是开发人员耳熟能详的知名定时任务工具,也是业内目前最优秀的同类产品,但是在使用 crontab 的过程中也有许多不便之处:
- 编辑任务时需要登录服务器,需要使用 vim 操作,虽然这对于开发人员和运维人员来说不是什么大问题,但是相对于使用 Web 界面操作来说还是麻烦了不少,如果能够使用 Web 界面可视化来操作,那会更加便捷与高效。
- crontab 的定时只支持到分钟级别,而 JTimer 支持到秒级。
- 在管理任务时,crontab 不支持分类管理,需要我们自己人为的分类,当任务数量很多时会显得有些杂乱,管理上略有不便。
- 有时候我们需要查询任务的执行日志,而 crontab 的执行日志存放在一个日志文件中,我们无法快速并且直观的筛选中我们想要的内容。
- 当有多台服务器需要部署或修改任务时,需要同时修改相应服务器配置。
JTimer 就是因为在使用 crontab 的过程中发现了这些不足,所以才研发出来的,那它的优势就很明显了。JTimer 的目的是提供更快速便捷的任务管理方式,以及任务日志的快速搜索。这是 JTimer 的优势和存在的意义,也是 JTimer 和 crontab 最大的不同之处。
JTimer 使用 Web 界面,让操作更加便捷、高效,下图展示了其管理页面:
任务列表:
编辑任务:
任务分类:
任务执行日志,可条件查询:
系统设置(Java 版暂无验证码与 ip 限制功能):
自动清除旧日志:
JTimer 的架构是怎么样的?工作流程又是怎么样的?
JTimer 可以大致分为两个模块,一是任务的管理模块,二是任务执行模块。
JTimer 分为 Java 版和 PHP 版,两者从管理模块上来说几乎完全一样,都提供了定时任务的 CRUD 和执行日志查询等功能,连 UI 都基本一致,不同之处在于任务执行模块的架构和实现机制。
JTimer for PHP
PHP 版任务执行模块使用了经典的 master-worker 进程模型。
- 有1个 master 进程负责监控整个系统的运行,保证所有进程的高可用。
- 有2个 worker 进程,其中一个负责监控任务的变化,另一个 worker 是一个定时器,负责任务的调度工作(使用了时间轮的算法),一旦有任务需要执行,就将任务传递给 task 进程执行,worker 本身不涉及任务的具体执行。两个 worker 之间以 tp 框架自带的文件缓存作为沟通的桥梁。
- n个 task 进程,负责任务的具体执行。
JTimer for Java
从工作流程上来说,Java 版和 PHP 版相差不多,只是具体的实现方式上不同。PHP 版使用的是多进程,而 Java 版使用的多线程:
- 两个线程,分别对应 PHP 版的的两个 worker,工作职责与 worker 一样。
- 任务的调度使用了 Spring 框架 CronTrigger。
- 任务的具体执行使用了 Java 原生的 Runtime.getRuntime().exec(),该方法本身是多进程方式执行任务,因此不需要我们参与进程的管理。
PHP 版与 Java 版相比较,Java 版在实现上大部分是使用了 Java 和 Spring 为我们提供的能力,因此较为简单。而 PHP 在这方面较为薄弱,无论是多进程方面还是定时器方面,都需要我们自己动手去实现,开发起来也复杂不少。
在 JTimer 研发的过程中,遇到的主要难点是什么?踩过什么坑?
开发 JTimer 的 PHP 版,从技术上来说并不难,并没有遇到太多困难。但在测试中遇到过一个问题,用了好几天才找到问题所在,下边简单讲一讲。
当时 JTimer 已经发布,有一位网友使用了 JTimer,但是在使用中,发现任务会被遗漏。例如有一个任务是每小时执行一次(整点执行),正常情况下一天会执行24次,但是实际上会被随机的遗漏掉1-2次,一天只执行了22或23次。
当这位网友把问题反馈给我之后,我查看了我这边的测试服务器上的 JTimer,却没有发现这个问题。后来这位网友说他的两台服务器,一台 CentOS 没有发现这个问题,一台 Ubuntu 却有这个问题。一开始我们以为是环境问题,排查了好几天,后来才发现是定时器的 bug。
导致 bug 的原因是:假设现在的时间是 00:00:00,当代码执行到“判断当前是否有任务需要执行”时,时间已经跳到 00:00:01 秒,导致判断错误,本应该在 00:00:00 执行的任务没有执行。
后来我解决这个问题的方法是在自己定义一个时钟,程序启动时获取当前时间戳保存下来,然后每秒加1。在要用到时间时都使用自己定义的这个时钟,而不用系统时间,这样就避免了时间差导致的任务丢失问题。
但是这样又产生了一个新的问题,理论上自定义的时钟和系统时间应该是一致的,但实际上每一秒会产生零点零零几毫秒的误差,累计下来几个小时就会产生一秒的误差。想要解决这个问题只能重构整个任务调度功能,该问题暂时还没有解决。不过仅 PHP 版有该问题,如果时间误差会影响业务,建议使用 Java 版。
目前在生产环境中使用该项目了吗?效果如果?或者测试效果如何?
目前已经有一个网友在生产环境中使用了 JTimer 的 PHP 版本,从效果上来说,除了上面所说的时间问题(Java 版没有该问题),暂时还没有发现别的问题。同时在此要感谢这位网友,在测试过程中帮我发现了一些问题(就是上个问题提到的网友)。
而从我自己的测试效果上来说,已经基本达到可以上线生产环境的标准。但毕竟测试样本太少,也许有尚未发现的 bug,广大开发者们在使用 JTimer 的过程中如果发现问题,希望能够反馈给我,帮助完善 JTimer。
介绍一下项目接下来的计划。
到目前为止项目的进展已经基本告一段落,并没有计划开发新的功能。JTimer 诞生的初衷是解决 crontab 管理不便的问题,该目的已经达到,我不希望将 JTimer 发展成一个庞大臃肿的项目,保持单一功能,能够解决同行们的痛点就够了。
不过前面提到的 PHP 版时间误差的问题仍需要解决的,这也是接下来唯一的计划,虽然 Java 版无此问题,但我还是希望能将 PHP 版做得更完美一些。同时,如果有意参与 JTimer 开发的开发者对它的发展有什么好的想法,也欢迎大家一起交流。
项目地址:https://gitee.com/itzhoujun/JTimer
嘉宾介绍
周君,资深后端工程师,博客作者,精通 PHP、Java、Web开发。多年实战经验,热衷分享。
精通PHP跟Java, 看你们怎么吐槽。 无论哪个是最好的语言,都是最牛逼的。
JTimer 这名子 一看还以为是java写的 所以一点用的欲望都没有~~
任务设置一个状态,比如是否已执行过,等等,可以解决时间点过后,任务被跳过的问题。
当然,还要有一个未被执行的任务队列。
我们做开发的,给别人开发了很多图形化管理系统,给自己的却是命令行。我很支持这样的图形化开发、运维工具!!!
这种东西就应该用golang啊不需要搭建环境直接运行
部署稍微有些麻烦。推荐一个国人开发的分布式任务管理系统。。。部署简单,支持多机,也带UI管理
https://github.com/shunfei/cronsun/blob/master/README_ZH.md
Rundeck 了解下