Перейти к основному содержимому

Планирование весной с Quartz

· 6 мин. чтения

1. Обзор

В этом уроке мы создадим простой планировщик Spring с помощью Quartz .

Мы начнем с простой цели — легко настроить новое запланированное задание.

1.1. Ключевые компоненты Quartz API

Quartz имеет модульную архитектуру. Он состоит из нескольких основных компонентов, которые мы можем комбинировать по мере необходимости. В этом уроке мы сосредоточимся на тех, которые являются общими для всех заданий: Job , JobDetail , Trigger и Scheduler . ``

Хотя мы будем использовать Spring для управления приложением, каждый отдельный компонент можно настроить двумя способами: способом Quartz или способом Spring (используя его удобные классы).

Мы рассмотрим оба варианта, насколько это возможно, для полноты картины, но мы можем принять любой из них. Теперь давайте начнем строить, по одному компоненту за раз.

2. Работа и JobDetail

2.1. Работа

API предоставляет интерфейс задания , который имеет только один метод — выполнить. Он должен быть реализован классом, который содержит реальную работу, которую необходимо выполнить, т. е. задачу. Когда срабатывает триггер задания, планировщик вызывает метод execute , передавая ему объект JobExecutionContext .

JobExecutionContext предоставляет экземпляру задания информацию о его среде выполнения, включая дескриптор планировщика, дескриптор триггера и объект задания JobDetail .

В этом кратком примере задание делегирует задачу классу обслуживания: ``

@Component
public class SampleJob implements Job {

@Autowired
private SampleJobService jobService;

public void execute(JobExecutionContext context) throws JobExecutionException {
jobService.executeSampleJob();
}
}

2.2. JobDetail

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

2.3. Кварц JobBuilder

Quartz JobBuilder предоставляет API в стиле конструктора для создания сущностей JobDetail :

@Bean
public JobDetail jobDetail() {
return JobBuilder.newJob().ofType(SampleJob.class)
.storeDurably()
.withIdentity("Qrtz_Job_Detail")
.withDescription("Invoke Sample Job service...")
.build();
}

2.4. Spring JobDetailFactoryBean

Spring JobDetailFactoryBean обеспечивает использование в стиле bean-компонента для настройки экземпляров JobDetail . Он использует имя bean-компонента Spring в качестве имени задания, если не указано иное:

@Bean
public JobDetailFactoryBean jobDetail() {
JobDetailFactoryBean jobDetailFactory = new JobDetailFactoryBean();
jobDetailFactory.setJobClass(SampleJob.class);
jobDetailFactory.setDescription("Invoke Sample Job service...");
jobDetailFactory.setDurability(true);
return jobDetailFactory;
}

При каждом выполнении задания создается новый экземпляр JobDetail . Объект JobDetail передает подробные свойства задания. После завершения выполнения ссылки на экземпляр удаляются.

3. Триггер

Триггер — это механизм планирования задания, т. е. экземпляр триггера «запускает» выполнение задания. Существует четкое разделение обязанностей между заданием (понятие задачи) и триггером (механизм планирования).

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

Допустим, мы хотим запланировать выполнение нашей задачи один раз в час на неопределенный срок, тогда мы можем использовать для этого TriggerBuilder Quartz или SimpleTriggerFactoryBean Spring .

3.1. Quartz TriggerBuilder

TriggerBuilder — это API в стиле конструктора для создания объекта Trigger :

@Bean
public Trigger trigger(JobDetail job) {
return TriggerBuilder.newTrigger().forJob(job)
.withIdentity("Qrtz_Trigger")
.withDescription("Sample trigger")
.withSchedule(simpleSchedule().repeatForever().withIntervalInHours(1))
.build();
}

3.2. Spring SimpleTriggerFactoryBean

SimpleTriggerFactoryBean обеспечивает использование в стиле bean-компонента для настройки SimpleTrigger . Он использует имя bean-компонента Spring в качестве имени триггера и по умолчанию использует бесконечное повторение, если не указано иное:

@Bean
public SimpleTriggerFactoryBean trigger(JobDetail job) {
SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean();
trigger.setJobDetail(job);
trigger.setRepeatInterval(3600000);
trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
return trigger;
}

4. Настройка магазина вакансий

JobStore предоставляет механизм хранения для задания и триггера. Он также отвечает за сохранение всех данных, относящихся к планировщику заданий. API поддерживает как in-memory, так и постоянные хранилища.

4.1. Магазин заданий в памяти ``

В нашем примере мы будем использовать хранилище RAMJobStore в памяти, которое предлагает молниеносную производительность и простую настройку с помощьюquart.properties :

org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore

Очевидным недостатком RAMJobStore является его непостоянство по своей природе. Вся информация о расписании теряется между отключениями. Если нам нужно сохранить определения заданий и расписания между отключениями, мы можем вместо этого использовать постоянный JDBCJobStore .

Чтобы включить хранилище заданий в памяти в Spring , мы установим это свойство в нашем application.properties :

spring.quartz.job-store-type=memory

4.2. Магазин вакансий JDBC ``

Существует два типа JDBCJobStore : JobStoreTX и JobStoreCMT . Оба они выполняют одну и ту же работу по хранению информации о расписании в базе данных.

Разница между ними заключается в том, как они управляют транзакциями, которые фиксируют данные. Тип JobStoreCMT требует транзакции приложения для хранения данных, тогда как тип JobStoreTX запускает и управляет своими собственными транзакциями.

Для JDBCJobStore необходимо задать несколько свойств . Как минимум, мы должны указать тип JDBCJobStore , источник данных и класс драйвера базы данных. Для большинства баз данных существуют классы драйверов, но StdJDBCDelegate охватывает большинство случаев:

org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.dataSource=quartzDataSource

Настройка JDBC JobStore в Spring выполняется в несколько шагов. Во-первых, мы установим тип хранилища в нашем application.properties :

spring.quartz.job-store-type=jdbc

Затем нам нужно включить автоматическую настройку и предоставить Spring источник данных, необходимый планировщику Quartz. Аннотация @QuartzDataSource выполняет за нас тяжелую работу по настройке и инициализации базы данных Quartz:

@Configuration
@EnableAutoConfiguration
public class SpringQrtzScheduler {

@Bean
@QuartzDataSource
public DataSource quartzDataSource() {
return DataSourceBuilder.create().build();
}
}

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

Интерфейс планировщика — это основной API для взаимодействия с планировщиком заданий.

Scheduler может быть создан с помощью SchedulerFactory . После создания мы можем зарегистрировать в нем Job и Trigger . Изначально планировщик находится в режиме «ожидания», и мы должны вызвать его метод запуска , чтобы запустить потоки, запускающие выполнение заданий.

5.1. Quartz StdSchedulerFactory

Просто вызвав метод getScheduler для StdSchedulerFactory , мы можем создать экземпляр Scheduler , инициализировать его (с настроенными JobStore и ThreadPool ) и вернуть дескриптор его API:

@Bean
public Scheduler scheduler(Trigger trigger, JobDetail job, SchedulerFactoryBean factory)
throws SchedulerException {
Scheduler scheduler = factory.getScheduler();
scheduler.scheduleJob(job, trigger);
scheduler.start();
return scheduler;
}

5.2. Spring SchedulerFactoryBean

SchedulerFactoryBean Spring обеспечивает использование в стиле bean-компонента для настройки Scheduler , управления его жизненным циклом в контексте приложения и предоставления Scheduler как bean-компонента для внедрения зависимостей:

@Bean
public SchedulerFactoryBean scheduler(Trigger trigger, JobDetail job, DataSource quartzDataSource) {
SchedulerFactoryBean schedulerFactory = new SchedulerFactoryBean();
schedulerFactory.setConfigLocation(new ClassPathResource("quartz.properties"));

schedulerFactory.setJobFactory(springBeanJobFactory());
schedulerFactory.setJobDetails(job);
schedulerFactory.setTriggers(trigger);
schedulerFactory.setDataSource(quartzDataSource);
return schedulerFactory;
}

5.3. Настройка SpringBeanJobFactory

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

Однако в нем отсутствует поддержка внедрения ссылок на bean-компоненты из контекста приложения . Благодаря автору этого сообщения в блоге , мы можем добавить в SpringBeanJobFactory поддержку автоматического подключения : ``

@Bean
public SpringBeanJobFactory springBeanJobFactory() {
AutoWiringSpringBeanJobFactory jobFactory = new AutoWiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}

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

В этой статье мы создали наш первый базовый планировщик, используя Quartz API, а также удобные классы Spring.

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

Полный исходный код примера доступен в этом проекте на github . Это проект Maven, поэтому мы можем импортировать его и запускать как есть. По умолчанию используются удобные классы Spring, но мы можем легко переключить его на Quartz API с параметром времени выполнения (см. README.md в репозитории).