Два Кварц-Работника, выполняющие одну и ту же работу дважды - PullRequest
4 голосов
/ 23 ноября 2011

Мы внедрили кварц для составления расписания. У каждой работы были разные ключи. До сих пор она работала нормально.Вчера мы столкнулись с проблемой, поскольку одно и то же задание выполняется дважды или трижды (без особого поведения) двумя разными потоками 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. Несколько потоков кварца, получающих одно и то же задание 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();
    }

1 Ответ

3 голосов
/ 02 декабря 2011

Интерфейс StatefulJob и аннотация @DisallowConcurrentExecution выполняют одно и то же.

Из Javadoc DisallowConcurrentExecution :

помечает класс Job как класс, который не должен иметь несколько экземпляров выполняется одновременно ....

Это можно использовать вместо реализации маркера StatefulJob интерфейс, который использовался до Quartz 2.0

Если для свойства threadPool.threadCount задано значение 1, это может означать максимум 1 задание любого типа может выполняться

Использование любого из этих решений остановит выполнение задания одновременно и приведет к тому, что любой триггер будет помещен в очередь для выполнения после завершения предыдущего экземпляра триггера

...