Статьи

Кварцевые планировщики плагинов — скрытое сокровище

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

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

LoggingTriggerHistoryPlugin

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

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

1
2
3
4
5
6
7
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. Добавление этих нескольких дополнительных строк значительно упрощает отладку и мониторинг:

1
2
3
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

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

1
2
3
4
5
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 для деталей и возможных заполнителей. Быстрый просмотр журналов показывает очень описательный вывод:

1
2
3
4
5
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 .

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

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
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:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?xml version="1.0" encoding="UTF-8"?>
                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 
    <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>

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

1
2
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 — ожидание текущих выполняемых заданий кажется плохой идеей. В конце концов, может быть, мы убиваем приложение, потому что некоторые задания выполняются слишком долго / слишком долго?

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

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

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

Ссылка: Кварцевые плагины планировщика — скрытое сокровище от нашего партнера JCG Томаша Нуркевича в блоге Java и соседей .