Статьи

Скрытые плагины Quartz Scheduler


Хотя вкратце это описано в официальной документации, я считаю, что
плагины Quartz недостаточно известны, если посмотреть, насколько они полезны.

По сути, плагины в Quartz — это удобные классы, заключающие в себе регистрацию базовых
слушателей . Вы можете написать свои собственные плагины, но мы сосредоточимся на существующих, поставляемых с Quartz.

LoggingTriggerHistoryPlugin

Сначала немного предыстории. Две основные абстракции в Кварце — рабочие места и триггеры. Работа — это кусок кода, который мы хотели бы запланировать. Trigger указывает планировщику, когда должен выполняться этот код. CRON (например, запускаются каждую пятницу с 9:00 до 17:00 до ноября ) и простые ( запускаются 100 раз каждые 2 часа ) триггеры наиболее часто используются. Вы связываете любое количество триггеров с одной работой.

Верьте или нет, Quartz по умолчанию не обеспечивает ведение журнала или мониторинг выполненных заданий и триггеров. Существует API, но встроенное ведение журнала не реализовано. Он не покажет вам, что теперь он выполняет эту конкретную работу из-за срабатывания триггера. Поэтому первое, что вы должны сделать, это добавить следующие строки в ваш quartz.properties:

org.quartz.plugin.triggerHistory.class=org.quartz.plugins.history.LoggingTriggerHistoryPlugin
 
org.quartz.plugin.triggerHistory.triggerFiredMessage=Trigger [{1}.{0}] fired job [{6}.{5}] scheduled at: {2, date, dd-MM-yyyy 
HH:mm:ss.SSS}, next scheduled at: {3, date, dd-MM-yyyy HH:mm:ss.SSS} org.quartz.plugin.triggerHistory.triggerCompleteMessage=Trigger [{1}.{0}] completed firing job [{6}.{5}] with resulting
trigger instruction code: {9}. Next scheduled at: {3, date, dd-MM-yyyy HH:mm:ss.SSS} org.quartz.plugin.triggerHistory.triggerMisfiredMessage=Trigger [{1}.{0}] misfired job [{6}.{5}]. Should have fired at:
{3, date, dd-MM-yyyy HH:mm:ss.SSS}

Первая строка (и единственная обязательная) загружает класс плагина LoggingTriggerHistoryPlugin . Остальные строки настраивают плагин, настраивая сообщения регистрации. Я обнаружил, что встроенные значения по умолчанию не очень хорошо продуманы, например, они отображают текущее время, которое уже является частью сообщения каркаса логирования. Вы можете создать любое сообщение регистрации, подробности см. В API. Добавление этих нескольких дополнительных строк значительно упрощает отладку и мониторинг: 

LoggingTriggerHistoryPlugin | Trigger [Demo.Every-few-seconds] fired job [Demo.Print-message] scheduled at: 
04-04-2012 23:23:47.036, next scheduled at: 04-04-2012 23:23:51.036 //...job output LoggingTriggerHistoryPlugin | Trigger [Demo.Every-few-seconds] completed firing job [Demo.Print-message] with resulting trigger
instruction code: DO NOTHING. Next scheduled at: 04-04-2012 23:23:51.036

Теперь вы понимаете, почему так важно присвоить имена своим триггерам (Demo.Every-two-seconds) и заданиям (Demo.Print-message).

LoggingJobHistoryPlugin

Есть еще один удобный плагин, связанный с регистрацией:

org.quartz.plugin.jobHistory.class=org.quartz.plugins.history.LoggingJobHistoryPlugin
org.quartz.plugin.jobHistory.jobToBeFiredMessage=Job [{1}.{0}] to be fired by trigger [{4}.{3}], re-fire: {7}
org.quartz.plugin.jobHistory.jobSuccessMessage=Job [{1}.{0}] execution complete and reports: {8}
org.quartz.plugin.jobHistory.jobFailedMessage=Job [{1}.{0}] execution failed with exception: {8}
org.quartz.plugin.jobHistory.jobWasVetoedMessage=Job [{1}.{0}] was vetoed. It was to be fired by trigger [{4}.{3}] at: 
{2, date, dd-MM-yyyy HH:mm:ss.SSS}

Правило то же самое — плагин + дополнительная настройка. Посмотрите JavaDoc LoggingJobHistoryPlugin для деталей и возможных заполнителей. Быстрый просмотр журналов показывает очень описательный вывод: 

Trigger [Demo.Every-few-seconds] fired job [Demo.Print-message] scheduled at:  04-04-2012 23:34:53.739, next scheduled at:  04-04-2012 23:34:57.739
Job [Demo.Print-message] to be fired by trigger [Demo.Every-few-seconds], re-fire: 0
//...job output
Job [Demo.Print-message] execution complete and reports: null
Trigger [Demo.Every-few-seconds] completed firing job [Demo.Print-message] with resulting trigger instruction code: DO NOTHING.
Next scheduled at: 04-04-2012 23:34:57.739

Я понятия не имею, почему эти плагины не включены по умолчанию. В конце концов, если вам не нужен такой подробный вывод, вы можете отключить его в своей структуре ведения журналов. Не берите в голову, я думаю, что это хорошая идея, чтобы иметь их на месте при устранении неполадок в исполнении Quartz.

XMLSchedulingDataProcessorPlugin

Это довольно полный плагин. Он читает XML-файл (по умолчанию с именем quartz_data.xml), содержащий задания, и запускает определения и добавляет их в планировщик. Это особенно полезно, когда у вас есть глобальная работа, которую нужно добавить один раз. Плагин может либо обновить существующие задания / триггеры, либо игнорировать файл XML, если они уже существуют, — очень полезно, когда используется JDBCJobStore .

org.quartz.plugin.xmlScheduling.class=org.quartz.plugins.xml.XMLSchedulingDataProcessorPlugin

В вышеупомянутой статье мы вручную добавляли работу в планировщик: 

val trigger = newTrigger().
        withIdentity("Every-few-seconds", "Demo").
        withSchedule(
            simpleSchedule().
                    withIntervalInSeconds(4).
                    repeatForever()
        ).
        build()
  
val job = newJob(classOf[PrintMessageJob]).
        withIdentity("Print-message", "Demo").
        usingJobData("msg", "Hello, world!").
        build()
  
scheduler.scheduleJob(job, trigger)

То же самое может быть достигнуто с помощью конфигурации XML, просто поместите следующий файл quartz_data.xml в ваш CLASSPATH: 

<?xml version="1.0" encoding="UTF-8"?>
<job-scheduling-data xmlns="http://www.quartz-scheduler.org/xml/JobSchedulingData"
                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                     xsi:schemaLocation=" http://www.quartz-scheduler.org/xml/JobSchedulingData http://www.quartz-scheduler.org/xml/job_scheduling_data_2_0.xsd ">
 
    <processing-directives>
        <overwrite-existing-data>false</overwrite-existing-data>
        <ignore-duplicates>true</ignore-duplicates>
    </processing-directives>
 
    <schedule>
        <trigger>
            <simple>
                <name>Every-few-seconds</name>
                <group>Demo</group>
                <job-name>Print-message</job-name>
                <job-group>Demo</job-group>
                <repeat-count>-1</repeat-count>
                <repeat-interval>4000</repeat-interval>
            </simple>
        </trigger>
 
        <job>
            <name>Print-message</name>
            <group>Demo</group>
            <job-class>com.blogspot.nurkiewicz.quartz.demo.PrintMessageJob</job-class>
            <job-data-map>
                <entry>
                    <key>msg</key>
                    <value>Hello, World!</value>
                </entry>
            </job-data-map>
        </job>
 
    </schedule>
 
 
</job-scheduling-data>

Файл поддерживает как простые триггеры, так и триггеры CRON и хорошо описан с использованием схемы XML . Можно даже указать на XML-файлы где-нибудь в файловой системе и периодически сканировать их на наличие изменений (!) (См. XMLSchedulingDataProcessorPlugin.setScanInterval () . Угадайте, что использует Quartz для планирования периодического сканирования? 

org.quartz.plugin.xmlScheduling.fileNames=/etc/quartz/system-jobs.xml,/home/johnny/my-jobs.xml
org.quartz.plugin.xmlScheduling.scanInterval=60

 

ShutdownHookPlugin

Наконец, что не менее важно , ShutdownHookPlugin . Небольшой, но, вероятно, полезный плагин, который регистрирует отключение в JVM, чтобы аккуратно остановить планировщик. Однако я рекомендую отключить cleanShutdown — если система уже пытается внезапно остановить приложение (обычно завершение работы планировщика вызывается Spring через SchedulerFactoryBean) или пользователь нажимает Ctrl + C — ожидание текущих выполняемых заданий кажется плохой идеей. В конце концов, может быть, мы убиваем приложение, потому что некоторые задания выполняются слишком долго / зависают?

org.quartz.plugin.shutdownHook.class=org.quartz.plugins.management.ShutdownHookPlugin
org.quartz.plugin.shutdownHook.cleanShutdown=false

Как вы можете видеть корабли Qurtz с несколькими довольно интересными плагинами. По некоторым причинам они не описаны подробно в официальной документации, но они работают довольно хорошо и являются ценным дополнением к планировщику.

Исходный код с примененными плагинами доступен на GitHub.