Недавно в процессе работы, я столкнулся с задачей управления шедулерами, в работающем приложении. У нас серверное приложение, и в конфигурационных файлах Spring мы указывали, какие задачи запускать по таймеру. Однако, далее появилась следующая задача — убирать из списка выполнения эти задачи, или же менять cron-таймер. При этом не тормозя приложение.
В процессе гугления, чтения, я нашел как это сделать. Всё оказалось гораздо проще, чем я думал. Но чтоб понять — пришлось немного почитать.
Вероятно эта статья будет полезна новичкам, но, возможно, и мастодонты почерпнут для себя что-то новое.
Используемые термины:
Шедулер — управляет таймерами запуска задач
Джоб — конкретная задача, запускаемая по таймеру
Триггер — условие выполнения задачи — задает временные рамки запуска задач и их выполнения
Соответственно шедулер содержит в себе описания всех задачи и триггеров к ним.
В системе используется шедулер, который описан в конфигурационном файле системы (shedulers.xml).
<beans:bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<beans:property name="jobDetails">
<beans:list>
<beans:ref bean="reportCheckServiceJob" />
<beans:ref bean="vacationApprovalAutoProcessServiceJob" />
<beans:ref bean="plannedVacationServiceJob" />
<beans:ref bean="employeeLdapServiceJob" />
<beans:ref bean="oqProjectSyncServiceJob" />
</beans:list>
</beans:property>
<beans:property name="triggers">
<beans:list>
<beans:ref bean="reportCheckServiceCronTrigger" />
<beans:ref bean="vacationApprovalAutoProcessServiceCronTrigger" />
<beans:ref bean="plannedVacationServiceCronTrigger" />
<beans:ref bean="employeeLdapServiceCronTrigger" />
<beans:ref bean="oqProjectSyncServiceCronTrigger" />
</beans:list>
</beans:property>
</beans:bean>
Пример джоб (и сервиса, метод которого он вызывает):
<beans:bean id="plannedVacationService"
class="com.aplana.timesheet.service.PlannedVacationService" />
<beans:bean id="plannedVacationServiceJob"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<beans:property name="targetObject" ref="plannedVacationService" />
<beans:property name="targetMethod" value="service" />
</beans:bean>
Где targetObject указывает класс-сервис (бин), в котором содержится метод, который необходимо запускать по таймеру. В targetMethod — указываем название метода.
Пример триггера:
<beans:bean id="plannedVacationServiceCronTrigger"
class="org.springframework.scheduling.quartz.CronTriggerBean">
<beans:property name="jobDetail" ref="plannedVacationServiceJob" />
<beans:property name="cronExpression" value="${scheduler.plannedvacationcheck}" />
</beans:bean>
В этом триггере мы указываем джоб, который будем выполнять по триггеру (в данном случае это plannedVacationServiceJob, тоже бин), а так же укажем в формате cron режим и периодичность запуска задачи.
Теперь о том, как управлять шедулером.
Для управления этим шедулером, необходимо в контроллере (сервисе) подключить этот бин (в нашем случае это SchedulerFactoryBean).
У этой фабрики есть конкретный используемый шедулер, его можно получить методом getScheduler(). И уже через него управлять джобами, триггерами да и самими задачами.
Рассмотрим методы шедулера для остановки и запуска джобов:
pauseJob(String name, String Group) — остановить выполнение задачи шедулера в указанной группе джобов. Остановка происходит путём остановки соответствующего триггера (см. pauseTrigger)
resumeJob(String name, String Group) — возобновить выполнение задачи шедулера в указанной группе джобов. Восстановление происходит путём запуска соответствующего триггера (см. resumeTrigger)
pauseTrigger(String name, String Group) — останавливает триггер в соответствующей группе
resumeTrigger(String name, String Group) — возобновляет работу триггера в соответсвующей группе
pauseAll — останавливает все задачи шедулера (pauseJobs(String group) — только у конкретной группы)
resumeAll — возобновляет запуск всех задач шедулера (см. также resumeJobs)
А так же ниже в коде приводится код как можно посмотреть все задачи у группы джобов, и список всех имеющихся групп в шедулере. По умолчанию, если у джобов не указаны группы, то им присваивается группа DEFAULT.
Можно автоварить (@Autowired) напрямую триггеры и управлять их планировщиком cron. В примере это plannedVacationServiceCronTrigger, у которого мы меняем CronExpression. Или же можно обратиться к триггеру через шедулер вызвав метод getTrigger(String name, String group).
@Autowired
@Qualifier("plannedVacationServiceCronTrigger")
private CronTriggerBean plannedVacationServiceCronTrigger;
@Autowired
private SchedulerFactoryBean schedulerFactoryBean;
public someMethod(){
logger.info(plannedVacationServiceCronTrigger.getCronExpression());
plannedVacationServiceCronTrigger.setCronExpression("*/1 * * * * ?");
//schedulerFactoryBean.getScheduler().pauseJob("plannedVacationServiceJob", "DEFAULT");
schedulerFactoryBean.getScheduler().pauseTrigger("plannedVacationServiceCronTrigger", "DEFAULT");
for (String str : schedulerFactoryBean.getScheduler().getJobGroupNames()){
logger.info(str);
}
for (String str : schedulerFactoryBean.getScheduler().getJobNames("DEFAULT")){
logger.info(str);
}
logger.info(schedulerFactoryBean.getScheduler().getTrigger("plannedVacationServiceJob", "DEFAULT").getCronExpression());
}
Что читать:
www.seostella.com/ru/article/2012/02/12/ispolzovanie-annotacii-autowired-v-spring-3.html
static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/scheduling.html
quartz-scheduler.org/api/2.1.0/org/quartz/Scheduler.html
ru.wikipedia.org/wiki/Cron
Автор: zildarius