Abh*_*tra 6 java scheduling quartz-scheduler
我们已经实现了quartz来进行调度。每个产生的作业都有不同的key。到目前为止一切正常。昨天我们遇到了一个问题,因为同一个作业被两个不同的 Quartz-Worker 线程执行了两次或三次(没有特殊行为)。我们不能将线程池大小设为一,因为我们需要并发作业。
关于我们的计划作业,值得注意的一件事是,它会在每次运行时自行重新安排(每日、每周或每月),即,如果安排作业每天运行,那么它将在接下来的 24 小时内重新安排自己,但会随机预定义(例如 3 小时)时间窗口。例如,如果某个作业今天在 4:10(即 4:00 到 7:00 之间)运行,那么我们的作业会将其自行重新安排到明天 4:00 到 7:00 之间的某个随机时间。它可以是 4:01 或 6:59 或 5:23 或给定时间窗口中的任何其他值。这个过程也运行良好,并且在大多数情况下仍然运行良好,但在某些情况下,我们的重新安排算法无法在接下来的 24 小时内自行安排。相反,它会在接下来的 10 秒、1 小时或任何其他随机值内自行安排。但在2-3次错误的重新安排之后,它最终稳定下来,即它最终在接下来的24小时内自行安排。我们怀疑这可能是由于多个线程访问 Calendar 对象而发生的(我们使用 Calendar.getInstance() 和 cal.add(Calendar.DAY_OF_YEAR, 1) 在接下来的 24 小时内重新安排作业)。不知何故,日历实例选择了错误的时间或无法在当前时间添加一天。
因此,存在两个问题: 1. 多个 Quartz 线程获取相同的作业 2. 日历无法添加给定的间隔或在某些特定情况下选择错误的当前时间(多线程访问)
任何帮助将不胜感激。尽快回复。谢谢。
感谢您的回复。我想知道 Statefuljob 和 @DisallowConcurrentExecution 注释以及将 threadPool.threadCount 设置为 1 之间有什么区别。
重新安排的代码如下...
Calendar cal = Calendar.getInstance();
Calendar nextCal = Calendar.getInstance();
cal.setTimeZone(TimeZone.getTimeZone(obj.getTimeZone()));
nextCal.setTimeZone(TimeZone.getTimeZone(obj.getTimeZone()));
Date startTime = null;
SimpleTrigger trigger = null;
JobDataMap dataMap = new JobDataMap();
if (repeatTimeInMillis == null) {
cal.set(Calendar.HOUR_OF_DAY, obj.getStartTime());
nextCal.set(Calendar.HOUR_OF_DAY, obj.getStartTime());
cal.set(Calendar.MINUTE, 0);
nextCal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
nextCal.set(Calendar.SECOND, 0);
if (obj.getScheduleType() == ScheduleType.MONTHLY) { // Monthly
log.info("in monthly schedule");
nextCal.add(Calendar.MONTH, 2);
nextCal.set(Calendar.DAY_OF_MONTH, obj.getDate());
cal.add(Calendar.MONTH, 1);
cal.set(Calendar.DAY_OF_MONTH, obj.getDate());
} else if (obj.getScheduleType() == ScheduleType.WEEKLY) { // Weekly
log.info("in weekly schedule");
nextCal.add(Calendar.WEEK_OF_YEAR, 2);
nextCal.set(Calendar.DAY_OF_WEEK, obj.getDay());
cal.add(Calendar.WEEK_OF_YEAR, 1);
cal.set(Calendar.DAY_OF_WEEK, obj.getDay());
} else if (obj.getScheduleType() == ScheduleType.DAILY) { // Daily
log.info("in daily schedule");
nextCal.add(Calendar.DAY_OF_YEAR, 2);
cal.add(Calendar.DAY_OF_YEAR, 1);
}
long time = obj.getTimeWindow() * 60 * 60 * 1000;
time = Math.round(time * Math.random());
cal.setTimeInMillis(cal.getTimeInMillis() + time);
startTime = cal.getTime();
nextCal.setTimeInMillis(nextCal.getTimeInMillis() + time);
repeatTimeInMillis = nextCal.getTimeInMillis() - cal.getTimeInMillis();
log.info("Rescheduling job at " + startTime);
trigger = newTrigger().usingJobData(dataMap)
.withIdentity(obj.getScheduleJobName(), obj.getScheduleJobGroup()).startAt(startTime)
.withSchedule(simpleSchedule().withIntervalInMilliseconds(repeatTimeInMillis).repeatForever())
.build();
} else {
log.info("Rescheduling job next " + repeatTimeInMillis + " milliseconds.");
cal.setTimeInMillis(cal.getTimeInMillis() + repeatTimeInMillis);
startTime = cal.getTime();
trigger = newTrigger().usingJobData(dataMap)
.withIdentity(obj.getScheduleJobName(), obj.getScheduleJobGroup()).startAt(startTime)
.withSchedule(simpleSchedule().withIntervalInMilliseconds(repeatTimeInMillis).withRepeatCount(1)).build();
}
Run Code Online (Sandbox Code Playgroud)
StatefulJob 接口和 @DisallowConcurrentExecution 注释执行相同的操作。
来自DisallowConcurrentExecution javadoc:
将 Job 类标记为不能同时执行多个实例的类......
这可以用来代替实现 Quartz 2.0 之前使用的 StatefulJob 标记接口
将 threadPool.threadCount 属性设置为 1 意味着最多可以执行1 个任何类型的作业
使用任何这些解决方案都将停止并发执行的作业,并导致任何触发器被放入队列中,以便在前一个触发器实例完成时执行
| 归档时间: |
|
| 查看次数: |
6066 次 |
| 最近记录: |