两个 Quartz-Worker 执行同一作业两次

发布于 2024-12-17 15:42:00 字数 3646 浏览 2 评论 0原文

我们已经实现了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();
    }

We have implemented quartz for scheduling.Every job produced have different key.It was working fine till now. Yesterday we come through a problem as same job is being executed twice or thrice(no particular behaviour) by two different Quartz-Worker threads.
We cant make thread pool size one as we need concurrent jobs.

One noticeable thing about our scheduled job is that it reschedules(daily, weekly or monthly) itself at every run i.e. if a job is scheduled to run daily then it would reschedule itself in next 24 hours but with a random predefined(say 3 hours) time window. For example if a job ran today at 4:10 (i.e between 4:00 and 7:00) then our job will reschedule it self to tomorrow at some random time between 4:00 and 7:00. It may be 4:01 or 6:59 or 5:23 or any other value in the given time window. This process was also working fine and it is still working fine for most of the cases except in some cases where our rescheduling algo is failing to schedule itself in next 24 hours. Instead it is scheduling itself in next 10 sec, 1 hr or any other random value. But it finally stabilizes itself after 2-3 such wrong rescheduling i.e. it finally schedule itself in next 24 hrs. We are suspecting that this may be happening due to the multiple threads accessing the Calendar object (we are using Calendar.getInstance() and cal.add(Calendar.DAY_OF_YEAR, 1) to reschedule the job in next 24 hours). Somehow calendar instance is picking wrong time or is not able to add one day in current time.

So, there are two issues:
1. Multiple Quartz threads acquiring the same job
2. Calendar is not able to add given interval or is picking wrong current time in some particular cases(multiple thread access)

Any help will be appreciated.Reply asap.
Thanks.


Thanks for reply.
I would like to know what is the difference between Statefuljob and @DisallowConcurrentExecution annotation and setting threadPool.threadCount to 1.

Code for rescheduling is as...

    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();
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

静待花开 2024-12-24 15:42:00

StatefulJob 接口和 @DisallowConcurrentExecution 注释执行相同的操作。

来自 DisallowConcurrentExecution javadoc

将 Job 类标记为不得有多个实例
同时执行....

这可以用来代替实现 StatefulJob 标记
Quartz 2.0 之前使用的接口

将 threadPool.threadCount 属性设置为 1 意味着最多可以执行 1 个任何类型的作业

使用这些解决方案中的任何一个都将停止并发执行的作业并导致任何触发被放入队列中,以便在前一个触发器实例完成时执行

The StatefulJob interface and @DisallowConcurrentExecution annotation do the same thing.

From the DisallowConcurrentExecution javadoc:

marks a Job class as one that must not have multiple instances
executed concurrently....

This can be used in lieu of implementing the StatefulJob marker
interface that was used prior to Quartz 2.0

Setting the threadPool.threadCount property to 1 would mean at most 1 job of any type can be executing

Using any of these solutions will stop a job executing concurrently and cause any trigger to be placed into a queue to be executed when the previous trigger instance has completed

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文