更多请关注 >>http://java.jr-jr.com

  1. 1. 介绍
  2. 2. Linux Crontab格式
  3. 3. Quartz Crontab格式

介绍

定时器大家都会用到,但JDK的Timer和ScheduledThreadPoolExecutor都只是提供固定频率的定时功能,很难满足业务需求。或多或少的,你肯定用过Linux的crontab和Java的Quartz。

Crontab的缺点是显而易见的,它是单机的,机器一旦宕了,一切都完蛋了。它的好处就是使用简单,可使用简单的脚本进行任务编写。

针对定时器的问题,一般集中在以下几个:

  • Quartz怎么做分布式调度
  • Quartz分布式,偶尔重复执行的问题
  • Quartz的表达式是怎么表示的
  • 有没有自己实现过调度系统,还有没有其他思路

Quartz使用分布式方式部署,需要每台服务器的时钟相同,最好是使用统一的ntp服务。多机部署的情况下,要保证每个时刻只有一台机器运行任务(比如一些退款服务,肯定不能多次执行),需要做一些分布式协调。最笨的方式就是使用Quartz自带的分布式方案,使用数据库锁进行任务选择;如果不想使用数据库环境,可以使用zookeeper或者redis分布式锁进行协调控制。需要注意的是,任何协调都不是100%准确的,业务方必须要做好任务多次执行的防重。

造成任务协调失败的情况很多,其中之一就是服务期间的时钟不同步,执行的步伐乱七八糟的,难受。另一种情况就是你的任务执行效率太高了,几毫秒就完成了,某台机器将任务执行完了进行回馈,于是另一台判断任务还没执行,继续领取任务,当然这是偶发情况。

调度系统一种思路是分布式的推,类似Quartz,每台服务器都在进行计算,触发,计算的轮回,通过远程调用进行触发。如果我想知道任务的执行进度,间或想提供一种更nice的配置方式给客户端,可以提供定制的jar包给业务方,业务方会定期的拉取(或者zk的通知)任务列表,进行相应的触发,同时提供汇报进度接口。

拉取任务的方式更加灵活,但比较复杂,尤其是在异构环境下,需要提供多种语言的客户端解决方案。在大多数情况下,Quartz就能够满足。

Linux Crontab格式

一个cron表达式有至少5个有空格分隔的时间元素。

1
2
3
4
5
6
7
+---------------- 分 (0 - 59)
| +------------- 时 (0 - 23)
| | +---------- 日 (1 - 31)
| | | +------- 月 (1 - 12)
| | | | +---- 周 (0 - 7) (Sunday=0 or 7)
| | | | |
* * * * * command to be executed

其中:

  • 逗号(‘,’) 指定列表值。如: “1,3,4,7,8”
  • 中横线(‘-‘) 指定范围值 如 “1-6”, 代表 “1,2,3,4,5,6”
  • 星号 (‘*’) 代表所有可能的值
  • Linux(开源系统似乎都可以)下还有个 “/“ 可以用. 在 Minute 字段上,*/15 表示每 15 分钟执行一次. 而这个特性在商业 Unix ,比如 AIX 上就没有.

Quartz Crontab格式

一个cron表达式有至少6个(也可能7个)有空格分隔的时间元素。
Cron表达式对特殊字符的大小写不敏感,对代表星期的缩写英文大小写也不敏感。

1
2
3
4
5
6
7
8
+------------------- 秒 (0 - 59)
| +---------------- 分 (0 - 59)
| | +------------- 时 (0 - 23)
| | | +---------- 日 (1 - 31)
| | | | +------- 月 (1 - 12)
| | | | | +---- 周 (0 - 7) (Sunday=0 or 7)
| | | | | | -- 年 (19702099)
* * * * * * * command to be executed

其中:

  • 逗号(‘,’) 指定列表值。如: “1,3,4,7,8”
  • 中横线(‘-‘) 指定范围值 如 “1-6”, 代表 “1,2,3,4,5,6”
  • 星号 (‘*’) 代表所有可能的值
  • 问号(?):该字符只在日期和星期字段中使用,它通常指定为“无意义的值”,相当于点位符;
  • 斜杠(/):x/y表达一个等步长序列,x为起始值,y为增量步长值。如在分钟字段中使用0/15,则表示为0,15,30和45秒,而5/15在分钟字段中表示5,20,35,50,你也可以使用*/y,它等同于0/y;
  • L:该字符只在日期和星期字段中使用,代表“Last”的意思,但它在两个字段中意思不同。L在日期字段中,表示这个月份的最后一天,如一月的31号,非闰年二月的28号;如果L用在星期中,则表示星期六,等同于7。但是,如果L出现在星期字段里,而且在前面有一个数值 X,则表示“这个月的最后X天”,例如,6L表示该月的最后星期五;
  • W:该字符只能出现在日期字段里,是对前导日期的修饰,表示离该日期最近的工作日。例如15W表示离该月15号最近的工作日,如果该月15号是星期六,则匹配14号星期五;如果15日是星期日,则匹配16号星期一;如果15号是星期二,那结果就是15号星期二。但必须注意关联的匹配日期不能够跨月,如你指定1W,如果1号是星期六,结果匹配的是3号星期一,而非上个月最后的那天。W字符串只能指定单一日期,而不能指定日期范围;
  • LW组合:在日期字段可以组合使用LW,它的意思是当月的最后一个工作日;
  • 井号(#):该字符只能在星期字段中使用,表示当月某个工作日。如6#3表示当月的第三个星期五(6表示星期五,#3表示当前的第三个),而4#5表示当月的第五个星期三,假设当月没有第五个星期三,忽略不触发;
  • C:该字符只在日期和星期字段中使用,代表“Calendar”的意思。它的意思是计划所关联的日期,如果日期没有被关联,则相当于日历中所有日期。例如5C在日期字段中就相当于日历5日以后的第一天。1C在星期字段中相当于星期日后的第一天。

后面这几个针对星期的参数,不好记,交流起来都困难。不到万不得已,鬼才用。