2009-12-19 1 views
21

Для моего приложения я создаю задания и планирую их с помощью CronTriggers. Каждое задание имеет только один триггер, и оба имени задания и имена триггеров одинаковы. Никакие задания не имеют триггера.Кварц Java, возобновляющий работу, несколько раз исключает его

Теперь, когда я создаю cron-триггер, подобный этому "0/1 * * * *?", который инструктирует задание выполнять каждую секунду, он работает нормально.

Проблема поднимается, когда я первый приостановить работу по телефону:

scheduler.pauseJob(jobName, jobGroup); 

, а затем возобновить работу после того, как, скажем, 50 секунд с:

scheduler.resumeJob(jobName, jobGroup); 

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

Я думал, что это было связано с настройкой по умолчанию для команды осечки, но даже после установки осечки instruciton триггера при создании этого:

trigger.setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING); 

То же самое происходит. Может кто-нибудь предложить способ исправить это?

ответ

27

CronTrigger работы, памятуя nextFireTime. После создания триггера инициализируется nextFireTime. При каждом запуске задания обновляется nextFireTime. Так как задание не срабатывает при приостановке nextFireTime остается «старым». Поэтому после возобновления задания триггер вернет каждое старое время запуска.

Проблема в том, что триггер не знает, что он приостановлен. Чтобы преодолеть это, это обработка пропуска зажигания. После возобновления заданий будет вызываться метод триггера updateAfterMisfire(), который исправляет nextFireTime. Но нет, если разница между nextFireTime и теперь меньше, чем misfireThreshold. Тогда метод никогда не вызывается. Значение этого порогового значения - 60 000. Таким образом, если ваш период паузы будет длиннее 60-х, все будет хорошо.

Поскольку у вас есть проблемы, я предполагаю, что это не так. ;) Чтобы обойти это, вы можете изменить порог или использовать простую обертку CronTrigger:

public class PauseAwareCronTrigger extends CronTrigger { 
    // constructors you need go here 

    @Override 
    public Date getNextFireTime() { 
     Date nextFireTime = super.getNextFireTime(); 
     if (nextFireTime.getTime() < System.currentTimeMillis()) { 
      // next fire time after now 
      nextFireTime = super.getFireTimeAfter(null); 
      super.setNextFireTime(nextFireTime); 
     } 
     return nextFireTime; 
    } 
} 
+4

Спасибо, ооочень много :) Это сработало как шарм. Кажется странным, что такая простая задача, как приостановка работы, создаст такие проблемы. –

5

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

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

+0

В javadoc для метода pauseJob говорится: «Приостановить JobDetail с заданным именем - приостановив все его текущие триггеры». поэтому я предполагаю, что триггер приостанавливается. Также не существует метода паузы для самого триггера. Или что-то похожее на него. Просто удаляет триггер с задания и повторно вставляет его в мой единственный вариант, чтобы приостановить работу? Я имею в виду, что это довольно тривиальная задача, которую хочет сделать ваш планировщик. Почему это не просто работает? –

+0

Хм, хорошая точка. Я нахожу, что с Кварцем, пытаясь разными вещами, пока что-то не работает, это самый продуктивный подход, поскольку он не всегда делает то, что он говорит на жестяне. – skaffman

+0

Это очень неприятно, так как нет простого способа просто удалить триггер с работы. Могу ли я работать с триггером? и если да, то как? Есть идеи? Я начинаю терять терпение с этим, я пытался часами :) –

1

С 1.6.5 по крайней мере (самая ранняя версия кварца на кончиках пальцев), планировщик имеет метод PauseTrigger, что принимает имя/группу в качестве параметров. Это означает, что вам не нужно иметь подкласс для каждого типа триггера, который вы используете, и вам не нужно делать фанки для удаления/вставки.

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