Рекомендации по настройке планировщика заданий
Все политики 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.
Эти политики следует применять, если необходимо просроченное задание выполнить как можно скорее, но один раз, а далее выполнять задание согласно расписанию. В такую категорию входят, например, задания по синхронизации почты (такие, как <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.
Эти политики следует применять для заданий, которые необходимо выполнять строго в определенное время. Например, есть задание по сбору статистики, запускаемое каждый день в 3 часа ночи, когда нет активных пользователей на сайте (используется CronTrigger). Это задание достаточно ресурсоемкое и длительное, и в рабочее время его запускать нельзя, потому что это может замедлить работу пользователей. В таком случае нужно применить политику CronTrigger.DoNothing. В итоге, если по какой-то причине задание не было выполнено вовремя, следующий запуск будет запланирован на 3 часа ночи следующих суток.
Конфигурирование Quartz
thread count
Если планировщик не успевает своевременно обрабатывать задания или некоторые задания не выполнились ни разу, то, возможно, поможет увеличение числа потоков Quartz. Для этого необходимо установить необходимое количество потоков в файле Web.config загрузчика приложения.
misfireThreshold
Если увеличение числа потоков Quartz нежелательно (например, из-за ограниченных ресурсов), то оптимизировать выполнение заданий может изменение настройки misfireThreshold в файле Web.config загрузчика приложения.
Например, есть приложение с числом заданий, намного большим, чем число потоков. При этом большинство заданий выполняются с достаточно малым интервалом (1 минута). Значение настройки misfireThreshold равно одной минуте, число потоков равно 3.
Также для большинства заданий используются политики из группы Run immediately and continue. Это означает следующее. Quartz для большинства заданий, у которых MISFIRE_INSTR не равно -1 и NEXT_FIRE_TIME меньше текущего времени на 1 минуту (60000 мс), будет регулярно устанавливать время следующего запуска на текущее время. Это означает, что будет теряться изначальный порядок запланированных заданий, потому что всем заданиям будет установлено время запуска на текущее время. Как следствие, увеличивается вероятность того, что Quartz чаще будет брать в работу одни и те же задания, при этом игнорируя другие.
На рисунке отображена очередь планировщика после 15 минут работы. Задания, у которых значение PREV_FIRE_TIME равно NULL, ни разу не выполнялись. Очевидно, что таких заданий достаточно много.
Затем следует увеличить значение misfireThreshold до 10 минут (очистив при этом PREV_FIRE_TIME в QRTZ_TRIGGERS).
После 15 минут работы планировщика очередь будет выглядеть следующим образом:
Очевидно, что ни разу не запущенных заданий стало намного меньше.
Увеличение misfireThreshold приводит к тому, что планировщик более равномерно выполняет задания. Другими словами, выполняются практически все задания из очереди. Очевидно, что из-за нехватки потоков планировщик не успевает каждое из заданий выполнять через минуту. Это видно по колонке [Last repeat interval], значение в которой равно NEXT_FIRE_TIME - PREV_FIRE_TIME, мин. Однако, при этом планировщик выполняет каждое из заданий.
batchTriggerAcquisitionMaxCount
Увеличение batchTriggerAcquisitionMaxCount может оптимизировать работу планировщика, если не используется кластеризованная конфигурация Quartz (используется один узел планировщика).
Политики Quartz для обработки не отработавших вовремя заданий
В Quartz существуют как политики, общие для всех типов триггеров, так и политики, специфичные для конкретного типа триггера. В таблице перечислены все политики, используемые для триггеров SimpleTrigger, CronTrigger и CalendarIntervalTrigger.
Политика Quartz | Значение MISFIRE_ |
Значение Terrasoft. |
Тип триггера |
---|---|---|---|
Ignore |
-1 | Ignore |
для всех типов |
Описание поведения Ignore Триггеры с Ignore Все просроченные задания Quartz попытается выполнить как можно скорее, после чего вернется к изначальному расписанию триггера. Например, запланировано задание с триггером Simple
Если планировщик был выключен с 8:50 до 9:20, то при включении Quartz попытается как можно скорее выполнить 2 просроченных задания (в 9:00 и 9:15). Далее он будет выполнять 8 оставшихся заданий по расписанию — в 9:30; 9:45 и т.д. |
|||
Smart |
0 | Smart |
для всех типов |
Описание поведения Smart Используется Quartz по умолчанию для всех типов триггеров. В зависимости от типа и конфигурации триггера Quartz выберет соответствующую политику. В псевдокоде ниже приведен алгоритм выбора для версии Quartz 2.3.2. |
|||
Simple |
1 | Fire |
Simple |
Описание поведения Simple Применяется для триггеров Simple Например, запланировано задание с триггером Simple
Если планировщик был выключен с 8:50 до 9:20, то при включении Quartz попытается как можно скорее выполнить задание. В результате, в 9:20 или чуть позже задание будет выполнено. |
|||
SimpleTrigger. |
2 | Reschedule |
SimpleTrigger |
Описание поведения Simple Планировщик пытается как можно скорее выполнить первое просроченное задание. Все остальные запуски триггера будут выполнены с интервалом REPEAT_ Например, запланировано задание с триггером Simple
Если планировщик был выключен с 8:50 до 9:20, то при включении Quartz выполнит первое просроченное задание, назначенное на 9:00, (из заданий в 9:00 и 9:15) в 9:20. Оставшиеся 9 запусков выполнит в 9:35, 9:50 и т.д. . |
|||
Simple |
3 | Reschedule |
Simple |
Описание поведения Simple Планировщик пытается как можно скорее выполнить первое просроченное задание. Остальные просроченные задания игнорируются. Планировщик выполняет оставшиеся задания, которые не были просрочены, с интервалом REPEAT_ Например, запланировано задание с триггером SimpleTrigger на 10 повторений. Исходные условия:
Если планировщик был выключен с 8:50 до 9:20, то при включении Quartz выполнит первое просроченное задание (из заданий в 9:00 и 9:15) в 9:20. Второе просроченное задание будет проигнорировано, и оставшиеся 8 запусков будут выполнены в 9:35, 9:50 и т.д. |
|||
Simple |
4 | Reschedule |
SimpleTrigger |
Описание поведения Simple Планировщик игнорирует просроченные задания и ждет следующего планового запуска задания. При наступлении времени следующего запуска будут выполнены оставшиеся не просроченные задания с интервалом REPEAT_ Например, запланировано задание с триггером Simple
Если планировщик был выключен с 8:50 до 9:20, то при включении Quartz выполнит оставшиеся 8 непросроченных заданий в 9:30; 9:45 и т.д. |
|||
Simple |
5 | Reschedule |
Simple |
Описание поведения Simple Планировщик будет ждать следующего времени запуска и выполнит все оставшиеся задания с интервалом REPEAT_ Например, запланировано задание с триггером Simple
Если планировщик был выключен с 8:50 до 9:20, то при включении Quartz выполнит все 10 заданий в 9:30; 9:45 и т.д. |
|||
Cron |
1 | - | Cron |
Описание поведения CronTrigger. Планировщик пытается как можно скорее выполнить первое просроченное задание. Остальные просроченные задания игнорируются. Оставшиеся непросроченные задания планировщик выполняет согласно с расписанием. Например, запланировано задание с триггером Cron |
|||
CronTrigger. |
2 | - | Cron |
Описание поведения Cron Планировщик игнорирует все просроченные задания. Оставшиеся непросроченные задания выполняются согласно с расписанием. Например, запланировано задание с триггером Cron |
|||
Calendar |
1 | - | Calendar |
Описание поведения Calendar Поведение аналогично Cron |
|||
Calendar |
2 | - | Calendar |
Описание поведения Calendar Поведение аналогично Cron |
Особенности работы с планировщиком при использовании горизонтального масштабирования описаны в статье Настроить горизонтальное масштабирование.