Подтвердить что ты не робот

Quartz: предотвращение одновременных экземпляров задания в jobs.xml

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

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

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

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


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

  • Используйте 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, чтобы заставить его работать так, как я хочу, поэтому я не вижу большого преимущества в его использовании.

  • Используйте TriggerListener, чтобы вернуть true из vetoJobExecution().

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

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

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

  • Внедрите собственный Trigger, который расширяет SimpleTrigger и содержит общее состояние, которое может запускать собственный TriggerListener.

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

4b9b3361

Ответ 1

когда ваше задание Quartz просыпается, вы можете сделать:

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
    }

Ответ 2

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

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

@DisallowConcurrentExecution
public class ColorJob implements Job {

Ответ 3

dimitrisli ответ не является полным, поэтому здесь.

Когда 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 another instance running, so leaving" + this);
                    return;
                }

            }

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

Ответ 4

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

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

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

Ответ 5

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

Спасибо, D

Ответ 6

Незначительное изменение к решению скарамуша.

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

}

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

Ответ 7

Если вы используете 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);
    }
    ...
}