кварц: предотвращение одновременных экземпляров задания в jobs.xml - PullRequest
42 голосов
/ 20 апреля 2010

Это должно быть действительно легко. Я использую Quartz под Apache Tomcat 6.0.18, и у меня есть файл jobs.xml , который настраивает мое запланированное задание, которое выполняется каждую минуту.

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

Есть ли способ указать это в jobs.xml (предотвратить одновременные экземпляры)?

Если нет, могу ли я предоставить общий доступ к одноэлементному хранилищу в памяти в реализации Job моего приложения (это через JobExecutionContext ?), Чтобы я мог обработать сам параллелизм? (и определить, работает ли предыдущий экземпляр)


обновление: После суеты в документах я рассмотрел пару подходов, но либо не знаю, как заставить их работать, либо возникли проблемы.

  1. Использование StatefulJob . Это предотвращает одновременный доступ ... но я не уверен, какие другие побочные эффекты возникнут, если я его использую, также я хочу избежать следующей ситуации:

    Предположим, что время запуска будет каждую минуту, т.е. триггер № 0 = в момент времени 0, триггер № 1 = 60000 мс, № 2 = 120000, № 3 = 180000 и т. Д., А триггер № 0 в момент времени 0 запускает мою работу, которая занимает 130000 мсек. С простым заданием это будет запускать триггеры № 1 и № 2, пока триггер задания № 0 все еще работает. С StatefulJob это будет запускать триггеры # 1 и # 2 по порядку, сразу после того, как # 0 заканчивается в 130000. Я не хочу этого, я хочу, чтобы # 1 и # 2 не запускались, и следующий триггер, запускающий задание, должен состоится в # 3 (180000 мс). Поэтому мне все еще нужно сделать что-то еще с StatefulJob, чтобы заставить его работать так, как я хочу, поэтому я не вижу большого преимущества в его использовании.

  2. Используйте TriggerListener для возврата true из vetoJobExecution ().

    Хотя реализация интерфейса кажется простой, мне нужно выяснить, как декларативно настроить один экземпляр TriggerListener. Не удается найти документы для XML-файла .

  3. Используйте static разделяемый потокобезопасный объект (например, семафор или любой другой), принадлежащий моему классу, который реализует Job.

    Мне не нравится идея использования синглетонов через ключевое слово static в Tomcat / Quartz, не уверен, есть ли побочные эффекты. Кроме того, я действительно не хочу, чтобы они были настоящими одиночками, просто что-то, что связано с определенным определением работы.

  4. Реализация моего собственного Триггера , который расширяет SimpleTrigger и содержит общее состояние, которое может запускать свой собственный TriggerListener.

    Опять же, я не знаю, как настроить файл XML для использования этого триггера вместо стандартного <trigger><simple>...</simple></trigger>.

Ответы [ 7 ]

60 голосов
/ 14 июня 2011

Есть еще одно более простое решение. Заданию может быть дана аннотация DisallowConcurrentExecution, которая предотвращает запуск нескольких одновременных экземпляров. Смотри документы здесь .

Ссылка продолжает ломаться, поэтому вот соответствующий образец.

@DisallowConcurrentExecution
public class ColorJob implements Job {
23 голосов
/ 27 апреля 2011

Димитрисли ответ не полный, так что вот мой.

Когда Quartz Job просыпается, он возвращает вам свой JobExecutionContext. Я предполагаю, что вы хотите пропустить задания с тем же триггером.

  List<JobExecutionContext> jobs = jobExecutionContext.getScheduler().getCurrentlyExecutingJobs();
            for (JobExecutionContext job : jobs) {
                if (job.getTrigger().equals(jobExecutionContext.getTrigger()) && !job.getJobInstance().equals(this)) {
                    logger.info("There's another instance running, so leaving" + this);
                    return;
                }

            }

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

18 голосов
/ 20 апреля 2010

когда ваша работа в Кварце проснется, вы можете сделать:

JobDetail existingJobDetail = sched.getJobDetail(jobName, jobGroup);
    if (existingJobDetail != null) {
        List<JobExecutionContext> currentlyExecutingJobs = (List<JobExecutionContext>) sched.getCurrentlyExecutingJobs();
        for (JobExecutionContext jec : currentlyExecutingJobs) {
            if(existingJobDetail.equals(jec.getJobDetail())) {
                //String message = jobName + " is already running.";
                //log.info(message);
                //throw new JobExecutionException(message,false);
            }
        }
        //sched.deleteJob(jobName, jobGroup); if you want to delete the scheduled but not-currently-running job
    }
5 голосов
/ 20 апреля 2010

Я выполнил нечто подобное, заставив мои классы заданий реализовать StatefulJob , что гарантирует, что никакие другие задания не начнутся до завершения текущего выполняемого задания.

Надеюсь, что помогает;)

PD: Я реализовал это, используя JBoss ... но я не думаю, что это имеет какое-либо значение.

4 голосов
/ 20 октября 2010

не могли бы вы установить задание в качестве StatefulJob, и для каждого создаваемого триггера установить MisfireInstruction для задания, чтобы оно не срабатывало, если оно пропущено? Не уверен, какой тип работы вы используете, но вам придется изучить некоторые инструкции misfireInstructions, доступные для вашего типа запуска.

Спасибо, D

3 голосов
/ 14 января 2015

Если вы используете org.springframework.scheduling.quartz.QuartzJobBean:

protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
    try {
        Scheduler scheduler = context.getScheduler();
        List<JobExecutionContext> jobs = scheduler.getCurrentlyExecutingJobs();
        for (JobExecutionContext job : jobs) {
            if (job.getTrigger().equals(context.getTrigger()) && job.getJobDetail() != context.getJobDetail()) {
                LOG.warn("Ignored!");
                return;
            }
        }
        ...
    } catch (SchedulerException e) {
        LOG.error("What a luck! :'(", e);
    }
    ...
}
1 голос
/ 09 апреля 2013

Небольшое отклонение от решения Скарамуша.

List<JobExecutionContext> jobs = jobExecutionContext.getScheduler().getCurrentlyExecutingJobs();
for (JobExecutionContext job : jobs) {
    if (job.getTrigger().equals(jobExecutionContext.getTrigger()) && !job.getFireInstanceId().equals(jobExecutionContext.getFireInstanceId()) {
        logger.info("There's another instance running, so leaving" + this);
        return;
    }

}

Решение scaramouche завершается ошибкой, когда для всех заданий JobExecution используется один экземпляр (возвращая синглтон с использованием пользовательского класса JobFactory вместо вызова newInstance () для каждого выполнения)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...