Рекомендации по настройке планировщика заданий
Glossary Item Box
Выбор политики Quartz по обработке не отработавших вовремя заданий
Все политики Quartz по обработке не отработавших вовремя заданий можно разделить условно на три группы: Ignore misfire policy (Игнорировать), Run immediately and continue (Выполнить немедленно и продолжать) и Discard and wait for next (Отменить и ожидать следующего задания). Далее приведены рекомендации, в каких случаях использовать ту или иную политику.
Ignore misfire policy
Эта политика представлена одноименной константой MisfireInstruction.IgnoreMisfirePolicy = -1. Ее рекомендуется применять в том случае, когда необходимо гарантировать, что все запуски триггера будут обязательно выполнены, даже если имеется несколько просроченных запусков триггера. Например, есть задание А с периодичностью в 2 минуты. Из-за нехватки рабочих потоков Quartz или выключения планировщика время следующего старта задания A (NEXT_FIRE_TIME) отстает на 10 минут от текущего времени. Если необходимо, чтобы все 5 просроченных запусков задания обязательно выполнились, то следует использовать политику IgnoreMisfirePolicy.
Эту политику рекомендуется применять для триггеров, которые при каждом запуске используют некоторые уникальные данные, и важно, чтобы все запуски триггера были рано или поздно выполнены. Например, есть задание B, которое выполняется с интервалом в 1 час и генерирует отчет за интервал времени от PREV_FIRE_TIME до PREV_FIRE_TIME + 1 час. При этом планировщик был выключен на 8 часов. В таком случае необходимо, чтобы после включения планировщика все 8 просроченных запусков задания B были в конечном итоге запущены, и таким образом сгенерированы отчеты за каждый из часов простоя.
Следует также отметить, что применение этой политики к триггерам, которые не привязаны к каким-либо уникальным данным, может привести к ненужному загромождению очереди планировщика и ухудшению общей производительности приложения. Например, для каждого из пользователей Сreatio настроена синхронизация почты Exchange с интервалом в 1 минуту. При этом 1.5 часа выполнялось обновление. После обновления Quartz, прежде чем приступить к заданиям, запланированным на текущее время, будет выполнять для каждого из пользователей синхронизацию почты 90 раз. Хотя достаточно выполнить просроченную синхронизацию почты однократно, а затем вернуться к выполнению задания согласно расписанию.
Run immediately and continue
К этой группе политик относятся:
- SimpleTrigger.FireNow;
- SimpleTrigger.RescheduleNowWithExistingRepeatCount;
- SimpleTrigger.RescheduleNowWithRemainingRepeatCount;
- CronTrigger.FireOnceNow;
- CalendarIntervalTrigger.FireOnceNow.
Подробно каждая политика описана в статье "Политики Quartz для обработки не отработавших вовремя заданий".
Эти политики следует применять, если необходимо просроченное задание выполнить как можно скорее, но один раз, а далее выполнять задание согласно расписанию. В такую категорию входят, например, задания по синхронизации почты (такие, как <user>@<server>_LoadExchangeEmailsProcess_<userId>, SyncImap_<user>@<server>_<userId>), а также задачи RemindingCountersJob, SyncWithLDAPProcess.
Например, для каждого из пользователей настроена синхронизация почты с интервалом 5 минут, запускаемая триггерами <user>@<server>_LoadExchengeEmailProcess_<userId>Trigger. При этом сайт обновлялся с 1:30 до 2:43. Когда сайт был запущен (в 2:43), время следующего запуска для триггеров <user>@<server>_LoadExchengeEmailsProcess_<userId>Trigger будет обновлено на текущее (т. е., на 2:43). Соответственно, просроченные задания будут запущены один раз в 2:43 и далее будут выполняться согласно расписанию (т. е., в 2:48, 2:53, 2:58 и т. д.).
Discard and wait for next
К этой группе политик относятся:
- SimpleTrigger.RescheduleNextWithRemainingCount;
- SimpleTrigger.RescheduleNextWithExistingCount;
- CronTrigger.DoNothing;
- CalendarIntervalTrigger.DoNothing.
Подробно каждая политика описана в статье "Политики Quartz для обработки не отработавших вовремя заданий".
Эти политики следует применять для заданий, которые необходимо выполнять строго в определенное время. Например, есть задание по сбору статистики, запускаемое каждый день в 3 часа ночи, когда нет активных пользователей на сайте (используется CronTrigger). Это задание достаточно ресурсоемкое и длительное, и в рабочее время его запускать нельзя, потому что это может замедлить работу пользователей. В таком случае нужно применить политику CronTrigger.DoNothing. В итоге, если по какой-то причине задание не было выполнено вовремя, следующий запуск будет запланирован на 3 часа ночи следующих суток.
Конфигурирование Quartz
thread count
Если планировщик не успевает своевременно обрабатывать задания или некоторые задания не выполнились ни разу, то, возможно, поможет увеличение числа потоков Quartz. Для этого необходимо установить необходимое количество потоков в файле Web.config загрузчика приложения:
<add key="quartz.threadPool.threadCount" value="5" />
Примечание
Файл Web.config загрузчика приложения расположен в корневом каталоге установленного приложения Creatio.
misfireThreshold
Если увеличение числа потоков Quartz нежелательно (например, из-за ограниченных ресурсов), то оптимизировать выполнение заданий может изменение настройки misfireThreshold в файле Web.config загрузчика приложения.
Например, есть приложение с числом заданий, намного большим, чем число потоков. При этом большинство заданий выполняются с достаточно малым интервалом (1 минута). Значение настройки misfireThreshold равно одной минуте, число потоков равно 3:
<add key="quartz.jobStore.misfireThreshold" value="60000" /> <add key="quartz.threadPool.threadCount" value="3" />
Также для большинства заданий используются политики из группы Run immediately and continue. Это означает следующее. Quartz для большинства заданий, у которых MISFIRE_INSTR не равно -1 и NEXT_FIRE_TIME меньше текущего времени на 1 минуту (60000 мс), будет регулярно устанавливать время следующего запуска на текущее время. Это означает, что будет теряться изначальный порядок запланированных заданий, потому что всем заданиям будет установлено время запуска на текущее время. Как следствие, увеличивается вероятность того, что Quartz чаще будет брать в работу одни и те же задания, при этом игнорируя другие.
На рис. 1 отображена очередь планировщика после 15 минут работы. Задания, у которых значение PREV_FIRE_TIME равно NULL, ни разу не выполнялись. Очевидно, что таких заданий достаточно много.
Рис. 1. — Очередь планировщика при значении misfireThreshold, равном 1 мин
Затем следует увеличить значение misfireThreshold до 10 минут (очистив при этом PREV_FIRE_TIME в QRTZ_TRIGGERS):
<add key="quartz.jobStore.misfireThreshold" value="600000" />
После 15 минут работы планировщика очередь будет выглядеть следующим образом (рис. 2).
Рис. 2. — Очередь планировщика при значении misfireThreshold, равном 10 мин
Очевидно, что ни разу не запущенных заданий стало намного меньше.
Увеличение misfireThreshold приводит к тому, что планировщик более равномерно выполняет задания. Другими словами, выполняются практически все задания из очереди. Очевидно, что из-за нехватки потоков планировщик не успевает каждое из заданий выполнять через минуту. Это видно по колонке [Last repeat interval], значение в которой равно NEXT_FIRE_TIME - PREV_FIRE_TIME, мин. Однако, при этом планировщик выполняет каждое из заданий.
batchTriggerAcquisitionMaxCount
Увеличение batchTriggerAcquisitionMaxCount может оптимизировать работу планировщика, если не используется кластеризованная конфигурация Quartz (используется один узел планировщика).