在关闭Springboot应用时,想要保证执行中的定时任务不被中断而导致程序数据错误,需要在Springboot关闭的时候,先停止定时任务的执行。
废话不多说,直接上代码,具体原理见代码注释:
package com.azhuzhu.job.config;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.stereotype.Component;
/**
* 系统关闭时,关闭定时任务的执行(等待运行中的任务执行完成)
* <p>监听ContextClosedEvent事件 会在SpringBoot应用关闭,准备销毁Context以及里面的Bean时调用</p>
* <p>因为Bean被销毁之后程序肯定无法执行了,所以需要在这之前触发关闭Quartz</p>
*
* @author azhuzhu
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class QuartzShutdownListener implements ApplicationListener<ContextClosedEvent> {
private final Scheduler quartzScheduler;
@Override
public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
try {
log.info("-------------------等待运行中的定时任务执行完成-------------------");
// Quartz框架提供的方法,等待所有任务执行完成之后关闭调度器Scheduler,等待期间不再触发新的任务
quartzScheduler.shutdown(true);
log.info("-------------------执行完成,Quartz关闭-------------------");
} catch (SchedulerException e) {
log.info("-------------------Quartz关闭失败-------------------");
}
}
}
新建一个测试用的Job:
package com.azhuzhu.job.demo;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.springframework.stereotype.Component;
/**
* 测试任务-执行时间60s
*
* @author azhuzhu
*/
@Slf4j
@Component
public class ShutdownWaitJob implements Job {
@Override
public void execute(JobExecutionContext context) {
int i = 60;
while (--i > 0) {
log.info("距离完成还有" + i + "秒");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
本地测试,将任务开启执行之后,在IDEA停止任务
2020-10-23 14:11:45.943 INFO 4852 --- [eduler_Worker-5] com.azhuzhu.job.demo.ShutdownWaitJob : 距离完成还有9秒
2020-10-23 14:11:46.944 INFO 4852 --- [eduler_Worker-5] com.azhuzhu.job.demo.ShutdownWaitJob : 距离完成还有8秒
Disconnected from the target VM, address: '127.0.0.1:61759', transport: 'socket'
2020-10-23 14:11:47.492 INFO 4852 --- [extShutdownHook] c.n.c.job.config.QuartzShutdownListener : -------------------等待运行中的定时任务执行完成-------------------
2020-10-23 14:11:47.492 INFO 4852 --- [extShutdownHook] org.quartz.core.QuartzScheduler : Scheduler clusteredScheduler_$_HIH-L-62831603432025356 shutting down.
2020-10-23 14:11:47.493 INFO 4852 --- [extShutdownHook] org.quartz.core.QuartzScheduler : Scheduler clusteredScheduler_$_HIH-L-62831603432025356 paused.
2020-10-23 14:11:47.944 INFO 4852 --- [eduler_Worker-5] com.azhuzhu.job.demo.ShutdownWaitJob : 距离完成还有7秒
2020-10-23 14:11:48.945 INFO 4852 --- [eduler_Worker-5] com.azhuzhu.job.demo.ShutdownWaitJob : 距离完成还有6秒
2020-10-23 14:11:49.945 INFO 4852 --- [eduler_Worker-5] com.azhuzhu.job.demo.ShutdownWaitJob : 距离完成还有5秒
2020-10-23 14:11:50.946 INFO 4852 --- [eduler_Worker-5] com.azhuzhu.job.demo.ShutdownWaitJob : 距离完成还有4秒
2020-10-23 14:11:51.947 INFO 4852 --- [eduler_Worker-5] com.azhuzhu.job.demo.ShutdownWaitJob : 距离完成还有3秒
2020-10-23 14:11:52.947 INFO 4852 --- [eduler_Worker-5] com.azhuzhu.job.demo.ShutdownWaitJob : 距离完成还有2秒
2020-10-23 14:11:53.948 INFO 4852 --- [eduler_Worker-5] com.azhuzhu.job.demo.ShutdownWaitJob : 距离完成还有1秒
2020-10-23 14:11:55.003 INFO 4852 --- [extShutdownHook] org.quartz.core.QuartzScheduler : Scheduler clusteredScheduler_$_HIH-L-62831603432025356 shutdown complete.
2020-10-23 14:11:55.003 INFO 4852 --- [extShutdownHook] c.n.c.job.config.QuartzShutdownListener : -------------------执行完成,Quartz关闭-------------------
2020-10-23 14:11:55.248 INFO 4852 --- [extShutdownHook] o.s.s.quartz.SchedulerFactoryBean : Shutting down Quartz Scheduler
2020-10-23 14:11:55.252 INFO 4852 --- [extShutdownHook] o.f.j.s.i.a.AbstractAsyncExecutor : Shutting down the async job executor [org.flowable.spring.job.service.SpringAsyncExecutor].
2020-10-23 14:11:55.252 INFO 4852 --- [uire-timer-jobs] o.f.j.s.i.a.AcquireTimerJobsRunnable : stopped async job due acquisition
2020-10-23 14:11:55.252 INFO 4852 --- [uire-async-jobs] o.f.j.s.i.a.AcquireAsyncJobsDueRunnable : stopped async job due acquisition
2020-10-23 14:11:55.252 INFO 4852 --- [et-expired-jobs] o.f.j.s.i.a.ResetExpiredJobsRunnable : stopped resetting expired jobs
2020-10-23 14:11:55.261 INFO 4852 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
2020-10-23 14:11:55.265 INFO 4852 --- [extShutdownHook] a.s.s.m.AbstractValidatingSessionManager : Disabled session validation scheduler.
2020-10-23 14:11:55.276 INFO 4852 --- [extShutdownHook] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} closing ...
2020-10-23 14:11:55.377 INFO 4852 --- [extShutdownHook] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} closed
Process finished with exit code 130
注意:如果要安全的退出应用,服务器上终止应用时,不能使用
kill -9
直接杀死进程,而是使用kill -15
通知应用自身去结束并释放资源。