Введение в кварц

1. Обзор

  • http://www.quartz-scheduler.org/ - это структура планирования заданий с открытым исходным кодом, полностью написанная на Java и предназначенная для использования как в приложениях J2SE , так и J2EE . Он предлагает большую гибкость, не жертвуя простотой. **

Вы можете создавать сложные графики для выполнения любой работы. Примеры, например, задачи, которые выполняются ежедневно, каждую пятницу, в 19:30. или только в последний день каждого месяца.

В этой статье мы рассмотрим элементы для создания работы с помощью Quartz API. Для введения в сочетании с Spring мы рекомендуем ссылку:/spring-quartz-schedule[Планирование весной с помощью Quartz]

2. Зависимости Maven

Нам нужно добавить следующую зависимость в pom.xml:

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.0</version>
</dependency>

Последнюю версию можно найти в https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.quartz-scheduler%22%20AND%20a%3A%22quartz%22 [Maven Центральный репозиторий.

3. Кварцевый API

Сердцем фреймворка является Scheduler . Он отвечает за управление средой выполнения для нашего приложения.

Для обеспечения масштабируемости Quartz основан на многопоточной архитектуре.

  • При запуске платформа инициализирует набор рабочих потоков ** , которые используются Scheduler для выполнения Jobs .

Вот как фреймворк может одновременно запускать много Jobs . Он также опирается на слабосвязанный набор компонентов управления ThreadPool для управления средой потоков.

Ключевые интерфейсы API:

  • Scheduler – основной API для взаимодействия с планировщиком

рамки ** Job – интерфейс, который будет реализован компонентами, которые мы хотим

выполнил ** JobDetail – используется для определения экземпляров __Job __s

  • Trigger – компонент, который определяет расписание, по которому

данная Job будет выполнена ** JobBuilder – используется для создания экземпляров JobDetail , которые определяют

экземпляры Jobs ** TriggerBuilder – используется для создания Trigger экземпляров

Давайте посмотрим на каждый из этих компонентов.

4. Планировщик

Прежде чем мы сможем использовать Scheduler , его необходимо создать. Для этого мы можем использовать фабрику SchedulerFactory :

SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();

Жизненный цикл Scheduler ограничен его созданием через SchedulerFactory и вызовом его метода shutdown () . После создания интерфейс Scheduler можно использовать для добавления, удаления и перечисления Jobs и Triggers , а также для выполнения других операций, связанных с планированием (например, приостановка триггера).

Однако Scheduler не будет действовать ни на один из триггеров, пока не будет запущен методом start ()

scheduler.start();

5. Работа

Job - это класс, который реализует интерфейс Job . У него есть только один простой метод:

public class SimpleJob implements Job {
    public void execute(JobExecutionContext arg0) throws JobExecutionException {
        System.out.println("This is a quartz job!");
    }
}

Когда срабатывает триггер Job’s , метод execute () вызывается одним из рабочих потоков планировщика.

Объект JobExecutionContext , передаваемый этому методу, предоставляет экземпляру задания информацию о его среде выполнения, дескриптор Scheduler , который его выполнил, дескриптор Trigger , который вызвал выполнение, объект JobDetail задания и несколько других элементов ,

Объект JobDetail создается клиентом Quartz во время добавления Job к Scheduler. По сути, это определение экземпляра задания _: _

JobDetail job = JobBuilder.newJob(SimpleJob.class)
  .withIdentity("myJob", "group1")
  .build();

Этот объект может также содержать различные настройки свойств для Job , а также JobDataMap , которые можно использовать для хранения информации о состоянии для данного экземпляра нашего класса задания.

5.1. JobDataMap

JobDataMap используется для хранения любого количества объектов данных, которые мы хотим сделать доступными для экземпляра задания при его выполнении. JobDataMap является реализацией интерфейса Java Map и имеет несколько дополнительных удобных методов для хранения и извлечения данных примитивных типов.

Вот пример помещения данных в JobDataMap при построении JobDetail перед добавлением задания в планировщик:

JobDetail job = newJob(SimpleJob.class)
  .withIdentity("myJob", "group1")
  .usingJobData("jobSays", "Hello World!")
  .usingJobData("myFloatValue", 3.141f)
  .build();

И вот пример того, как получить доступ к этим данным во время выполнения задания:

public class SimpleJob implements Job {
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDataMap dataMap = context.getJobDetail().getJobDataMap();

        String jobSays = dataMap.getString("jobSays");
        float myFloatValue = dataMap.getFloat("myFloatValue");

        System.out.println("Job says: " + jobSays + ", and val is: " + myFloatValue);
    }
}

В приведенном выше примере будет напечатано «Job говорит Hello World !, а val равно 3.141».

Мы также можем добавить методы установки в наш класс заданий, который соответствует именам ключей в JobDataMap.

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

6. Триггеры

Объекты Trigger используются для запуска исполнения Jobs .

Когда мы хотим запланировать Job , нам нужно создать экземпляр триггера и настроить его свойства для настройки наших требований к планированию:

Trigger trigger = TriggerBuilder.newTrigger()
  .withIdentity("myTrigger", "group1")
  .startNow()
  .withSchedule(SimpleScheduleBuilder.simpleSchedule()
    .withIntervalInSeconds(40)
    .repeatForever())
  .build();

Trigger также может иметь JobDataMap , связанный с ним. Это полезно для передачи параметров Job , которые относятся к выполнению триггера.

Существуют разные типы триггеров для разных задач планирования.

Каждый из них имеет различные свойства TriggerKey для отслеживания их личности. Однако некоторые другие свойства являются общими для всех типов триггеров:

  • Свойство jobKey указывает идентификатор задания, которое должно быть

выполняется, когда срабатывает триггер.

  • Свойство startTime указывает, когда сначала запускается расписание триггера.

вступает в силу. Значением является объект java.util.Date , который определяет момент времени для данной календарной даты. Для некоторых типов триггеров триггер срабатывает в указанное время запуска. Для других это просто отмечает время, когда должно начаться расписание.

  • Свойство endTime указывает, когда расписание триггера должно быть

отменен.

Кварц поставляется с несколькими различными типами триггеров, но наиболее часто используемыми являются SimpleTrigger и CronTrigger .

6.1. Приоритет

Иногда, когда у нас много триггеров, у Quartz может не хватить ресурсов для немедленного запуска всех заданий, запланированных для запуска одновременно. В этом случае мы можем захотеть контролировать, какой из наших триггеров будет доступен первым. Это именно то, для чего используется свойство priority в триггере.

  • Например ** , когда одновременно запускаются десять триггеров и доступны только четыре рабочих потока, первые четыре триггера с наивысшим приоритетом будут выполняться первыми. Когда мы не устанавливаем приоритет для триггера, он использует приоритет по умолчанию, равный пяти. Любое целочисленное значение допускается как приоритетное, положительное или отрицательное.

В приведенном ниже примере у нас есть два триггера с различным приоритетом. Если ресурсов для одновременного срабатывания всех триггеров недостаточно, triggerA будет срабатывать первым:

Trigger triggerA = TriggerBuilder.newTrigger()
  .withIdentity("triggerA", "group1")
  .startNow()
  .withPriority(15)
  .withSchedule(SimpleScheduleBuilder.simpleSchedule()
    .withIntervalInSeconds(40)
    .repeatForever())
  .build();

Trigger triggerB = TriggerBuilder.newTrigger()
  .withIdentity("triggerB", "group1")
  .startNow()
  .withPriority(10)
  .withSchedule(SimpleScheduleBuilder.simpleSchedule()
    .withIntervalInSeconds(20)
    .repeatForever())
  .build();

6.2. Инструкции по осечке огня

  • Пропуск зажигания происходит, если постоянный триггер misses свое время срабатывания из-за закрытия Scheduler , или в случае отсутствия доступных потоков в пуле потоков Quartz. **

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

Давайте посмотрим на примеры ниже:

Trigger misFiredTriggerA = TriggerBuilder.newTrigger()
  .startAt(DateUtils.addSeconds(new Date(), -10))
  .build();

Trigger misFiredTriggerB = TriggerBuilder.newTrigger()
  .startAt(DateUtils.addSeconds(new Date(), -10))
  .withSchedule(SimpleScheduleBuilder.simpleSchedule()
    .withMisfireHandlingInstructionFireNow())
  .build();

Мы запланировали запуск триггера 10 секунд назад (так что к моменту его создания задержка составляет 10 секунд) для имитации пропуска зажигания, например потому что планировщик не работал или не имел достаточного количества рабочих потоков. Конечно, в реальном сценарии мы никогда не планировали бы триггеры, подобные этому.

В первом триггере ( misFiredTriggerA ) не заданы инструкции по обработке пропуска зажигания. Следовательно, в этом случае используется вызываемая smart policy и называется: withMisfireHandlingInstructionFireNow () . Это означает, что задание выполняется сразу после того, как планировщик обнаруживает пропуски зажигания.

Второй триггер явно определяет, какое поведение мы ожидаем при возникновении пропуска зажигания. В этом примере это просто та же самая умная политика.

6.3. SimpleTrigger

  • SimpleTrigger используется для сценариев, в которых нам нужно выполнить задание в определенный момент времени. ** Это может быть либо один раз, либо несколько раз через определенные интервалы

Примером может быть запуск выполнения задания точно в 12:20:00 13 января 2018 года. Точно так же мы можем начать в это время, а затем еще пять раз, каждые десять секунд.

В приведенном ниже коде дата myStartTime ранее была определена и используется для создания триггера для одной конкретной отметки времени :

SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
  .withIdentity("trigger1", "group1")
  .startAt(myStartTime)
  .forJob("job1", "group1")
  .build();

Далее, давайте создадим триггер для определенного момента времени, а затем повторяем каждые десять секунд десять раз:

SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
  .withIdentity("trigger2", "group1")
  .startAt(myStartTime)
  .withSchedule(simpleSchedule()
    .withIntervalInSeconds(10)
    .withRepeatCount(10))
  .forJob("job1")
  .build();

6.4. CronTrigger

  • CronTrigger используется, когда нам нужны расписания, основанные на календарных операторах. ** Например, мы можем указать расписания запуска, такие как every Friday at the don или every будний день в 9:30 am .

Выражения Cron используются для настройки экземпляров CronTrigger . Эти выражения состоят из Strings , которые состоят из семи подвыражений. Мы можем прочитать больше о Cron-выражениях https://docs.oracle.com/cd/E12058 01/doc/doc.1014/e12030/cron expressions.htm[here].

В приведенном ниже примере мы создаем триггер, который срабатывает каждую минуту с 8 до 17 часов каждый день:

CronTrigger trigger = TriggerBuilder.newTrigger()
  .withIdentity("trigger3", "group1")
  .withSchedule(CronScheduleBuilder.cronSchedule("0 0/2 8-17 **  **  ?"))
  .forJob("myJob", "group1")
  .build();

7. Заключение

В этой статье мы показали, как создать Scheduler для запуска Job . Мы также видели некоторые из наиболее распространенных вариантов запуска:

SimpleTrigger и CronTrigger .

Кварц можно использовать для создания простых или сложных расписаний для выполнения десятков, сотен или даже более заданий. Более подробную информацию о платформе можно найти на главном website .

Исходный код примеров можно найти по адресу over на GitHub .