- все рабочие потоки были заняты выполнением других заданий (возможно, с более высоким приоритетом)
- сам планировщик не работал
- задание было запланировано с начальным временем в прошлом (вероятно, ошибка кодирования)
Вы можете увеличить количество рабочих потоков, просто настроив org.quartz.threadPool.threadCount
в quartz.properties
(по умолчанию 10). Но вы не можете ничего сделать, когда все приложение / сервер / планировщик не работает. Ситуация, когда Кварц был неспособен к срабатыванию данного триггера, называется пропуском огня. Знаете ли вы, что делает Кварц, когда это происходит? Оказывается, существуют различные стратегии (так называемые инструкции по пропускам зажигания ), которые может использовать Quartz, а также есть некоторые настройки по умолчанию, если вы не задумывались об этом. Но для того, чтобы сделать ваше приложение надежным и предсказуемым (особенно при большой нагрузке или обслуживании), вы должны действительно убедиться, что ваши триггеры и задания сконфигурированы четко.
Существуют различные параметры конфигурации (доступные инструкции по пропускам зажигания ) в зависимости от выбранного триггера. Кроме того, Quartz ведет себя по-разному в зависимости от настройки триггера (так называемая интеллектуальная политика ). Хотя инструкции по пропускам зажигания описаны в документации, мне было трудно понять, что они на самом деле означают. Итак, я создал эту небольшую сводную статью.
Прежде чем углубиться в детали, необходимо описать еще один вариант конфигурации. Это org.quartz.jobStore.misfireThreshold
(в миллисекундах), по умолчанию 60000 (минута). Он определяет, как поздно триггер должен считаться пропущенным . При настройке по умолчанию, если триггер предполагалось сработать 30 секунд назад, Quartz с радостью просто запустит его. Такая задержка не считается пропуском зажигания. Однако, если триггер обнаружен через 61 секунду после запланированного времени — специальный поток обработчика пропуска зажигания позаботится об этом, подчиняясь инструкции пропуска зажигания. В целях тестирования мы установим этот параметр на 1000 (1 секунда), чтобы мы могли быстро проверить пропуски зажигания.
Простой триггер без повторения
В нашем первом примере мы увидим, как сбои обрабатываются простыми триггерами, запланированными для запуска только один раз:
1
2
3
|
val trigger = newTrigger(). startAt(DateUtils.addSeconds( new Date(), - 10 )). build() |
Тот же триггер, но с явно установленным обработчиком инструкций пропуска зажигания:
1
2
3
4
5
6
7
|
val trigger = newTrigger(). startAt(DateUtils.addSeconds( new Date(), - 10 )). withSchedule( simpleSchedule(). withMisfireHandlingInstructionFireNow() //MISFIRE_INSTRUCTION_FIRE_NOW ). build() |
В целях тестирования я просто планирую запуск триггера 10 секунд назад (так что к моменту создания он опаздывает на 10 секунд). В реальном мире вы обычно никогда не запланировали бы триггеры подобным образом. Вместо этого представьте, что триггер был установлен правильно, но к тому времени, когда он был запланирован, планировщик не работал или не имел свободных рабочих потоков. Тем не менее, как Кварц справится с этой необычной ситуацией? В первом фрагменте кода выше инструкция по обработке пропусков зажигания не установлена (в этом случае используется так называемая умная политика ). Второй фрагмент кода явно определяет, какое поведение мы ожидаем, когда происходит сбой. Смотрите таблицу:
Простой триггер повторяется фиксированное количество раз
Этот сценарий намного сложнее. Представьте, что мы запланировали какую-то работу повторить фиксированное количество раз:
1
2
3
4
5
6
7
8
9
|
val trigger = newTrigger(). startAt(dateOf( 9 , 0 , 0 )). withSchedule( simpleSchedule(). withRepeatCount( 7 ). withIntervalInHours( 1 ). WithMisfireHandlingInstructionFireNow() //or other ). build() |
В этом примере предполагается, что триггер срабатывает 8 раз (первое выполнение + 7 повторений) каждый час, начиная с startAt(dateOf(9, 0, 0))
сегодня ( startAt(dateOf(9, 0, 0))
. Таким образом, последнее выполнение должно произойти в 16:00. Однако Предположим, что по какой-то причине планировщик был не в состоянии выполнять задания в 9 и 10 часов утра, и он обнаружил этот факт в 10:15 утра, то есть произошел сбой 2 разрядов. Как поведет себя планировщик в этой ситуации?
Простой триггер повторяется бесконечно
В этом сценарии триггер повторяется бесконечное количество раз с заданным интервалом:
1
2
3
4
5
6
7
8
9
|
val trigger = newTrigger(). startAt(dateOf( 9 , 0 , 0 )). withSchedule( simpleSchedule(). withRepeatCount(SimpleTrigger.REPEAT _ INDEFINITELY). withIntervalInHours( 1 ). WithMisfireHandlingInstructionFireNow() //or other ). build() |
И снова триггер должен срабатывать каждый час, начиная с 9 часов утра сегодня ( startAt(dateOf(9, 0, 0))
. Однако планировщик не был способен выполнять задания в 9 и 10 часов утра и обнаружил этот факт в 10:15. AM, т. Е. 2 срабатывания сработали. Это более общая ситуация по сравнению с простым триггером, запускаемым фиксированное количество раз.
CRON триггеры
Триггеры CRON являются наиболее популярными среди пользователей Quartz. Однако есть также два других доступных триггера: DailyTimeIntervalTrigger
(например, срабатывание каждые 25 минут ) и CalendarIntervalTrigger
(например, срабатывание каждые 5 месяцев ). Они поддерживают политики запуска, невозможные как в CRON, так и в простых триггерах. Однако они понимают те же инструкции по обработке пропуска зажигания, что и триггер CRON.
1
2
3
4
5
6
|
val trigger = newTrigger(). withSchedule( cronSchedule( "0 0 9-17 ? * MON-FRI" ). withMisfireHandlingInstructionFireAndProceed() //or other ). build() |
В этом примере триггер должен срабатывать каждый час с 9:00 до 17:00 с понедельника по пятницу. Но еще раз первые два вызова были пропущены (поэтому триггер сработал), и эта ситуация была обнаружена в 10:15. Обратите внимание, что доступные команды пропуска зажигания отличаются от простых триггеров:
QTZ-283 Примечание : QTZ-283: MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY не работает с JDBCJobStore — очевидно, существует ошибка при использовании JDBCJobStore
, следите за этой проблемой.
Как видите, различные триггеры ведут себя по-разному в зависимости от фактической настройки. Более того, несмотря на то, что предусмотрена так называемая « умная политика» , зачастую решение основывается на требованиях бизнеса. По сути, есть три основные стратегии: игнорировать , запустить немедленно и продолжить, сбросить и ждать следующего . Все они имеют разные варианты использования:
Используйте политики игнорирования , если вы хотите убедиться, что все запланированные выполнения были запущены, даже если это означает, что несколько сработавших триггеров сработают. Подумайте о работе, которая генерирует отчет каждый час на основе заказов, размещенных в этот последний час. Если сервер не работал в течение 8 часов, вы все равно хотите, чтобы отчеты генерировались как можно скорее. В этом случае политики игнорирования будут просто запускать все триггеры, запланированные в течение этих 8 часов, так быстро, как это может планировщик. Они опоздают на несколько часов, но в итоге будут казнены.
Используйте политики * сейчас, когда есть задания, выполняемые периодически, а в случае пропуска зажигания они должны запускаться как можно скорее, но только один раз. Подумайте о работе, которая очищает каталог /tmp
каждую минуту. Если планировщик был занят в течение 20 минут и, наконец, может выполнить это задание, вы не хотите запускать его 20 раз! Одного достаточно, но убедитесь, что он работает так быстро, как может. Затем вернитесь к обычным минутным интервалам.
Наконец, следующие * правила хороши, если вы хотите, чтобы ваша работа выполнялась в определенные моменты времени. Например, вам нужно получать цены на акции каждый квартал. Они быстро меняются, поэтому, если ваша работа не сработала, а уже прошло 20 минут после часа, не беспокойтесь. Вы пропустили правильное время на 5 минут, и теперь вам все равно. Лучше иметь пробел, чем неточную стоимость. В этом случае Quartz пропустит все пропущенные исполнения и просто дождется следующего.
Справка: инструкции по пропуску кварцевого планировщика, объясненные нашим партнером по JCG Томашем Нуркевичем в блоге Java и соседей .