无论是使用Linux自带crontab程序,还是使用cron-utils的crontab语法解析,都存在整点运行问题。
问题来源
使用crontab语法的时候,如果是固定间隔运行,如
0 0/5 * * * *
30 */30 * * * *
其中分钟间隔只能是60的因子,如果不是60的因子,那么存在整点运行问题。如运行计划为
10 0/29 * * * *
那么运行时刻可能为
Mon Oct 09 15:29:10 CST 2017
Mon Oct 09 15:58:10 CST 2017
Mon Oct 09 16:00:10 CST 2017
...
这里的运行计划,多出来一个16:00:10,这是不在我们的运行计划中的。
解决问题
作者使用的是cron-utils,所以可以使用程序控制,实现规避整点运行的问题。
计算crontab时间间隔
代码先上:
/**
* @DESC caluate the max seperates
*/
public static long calcMaxSep(ExecutionTime executionTime,
DateTime updateCurrentFireTime) {
DateTime nextFireTime = executionTime
.nextExecution(updateCurrentFireTime);
DateTime nextFireTime1 = executionTime.nextExecution(nextFireTime);
long b1 = nextFireTime.toDate().getTime()
- updateCurrentFireTime.toDate().getTime();
long b2 = nextFireTime1.toDate().getTime()
- updateCurrentFireTime.toDate().getTime();
b2 = b2 / 2;
long b3 = nextFireTime1.toDate().getTime()
- nextFireTime.toDate().getTime();
return getMaxByTri(b1, b2, b3);
}
/**
* @DESC get max of input three numbers
*/
public static long getMaxByTri(long b1, long b2, long b3) {
// return b1 > b2 ? (b1 > b3 ? b1 : b3) : (b2 > b3 ? b2 : b3);
return Math.max(b1, Math.max(b2, b3));
}
基本原理:三个运行时刻,不可能都存在整点运行问题,除非设置就是整点运行的。分析三个运行时间之间的时间间隔,如果是正常区间,那三个时间应该是一致的;如果区间内或边界存在整点问题,那么应该可以通过该方法解决。作者通过该方法获取运行时间间隔,没有发现问题。
通过间隔获取下一个运行时刻
public static void updateTask(SchedulerTask task) {
task.setCurrentFiredTime(task.getNextFiredTime());// 直接获取计算好的下次执行时间,不再重新计算
DateTime updateCurrentFireTime = new DateTime(task.getNextFiredTime());
DateTime nextFireTime = nextFireTime(updateCurrentFireTime,Long.parseLong(task.getDescribe()));
task.setNextFiredTime(nextFireTime.toDate());
}
public static int adaptTask(SchedulerTask task, Date time) {
// 调整任务,需要重新计算
Cron quartzCron = getCron(task.getCron());
ExecutionTime executionTime = ExecutionTime.forCron(quartzCron);
DateTime updateCurrentFireTime = new DateTime(time);
updateCurrentFireTime = executionTime.nextExecution(new DateTime());
task.setCurrentFiredTime(updateCurrentFireTime.toDate());
DateTime nextFireTime = nextFireTime(updateCurrentFireTime,Long.parseLong(task.getDescribe()));
task.setNextFiredTime(nextFireTime.toDate());
return isExecutable(task, time);
}
public static DateTime nextFireTime(DateTime currentFireTime, long added) {
return currentFireTime.withDurationAdded(added, 1);
}
如果正常运行,通过时间间隔获取下一个运行时间点;如果运行异常,重新计算初始运行时间点。
限制条件
- 本方法适用于周期运行的时间点
- 本方法不适用于日、星期、月、年等存在区间限制,也即crontab表达式,后面几个都是”*”
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!