Spring 关闭TaskScheduler不会停止运行@Scheduled方法
我有一个类,其方法注释为@ScheduledSpring 关闭TaskScheduler不会停止运行@Scheduled方法,spring,spring-boot,taskscheduler,spring-scheduled,Spring,Spring Boot,Taskscheduler,Spring Scheduled,我有一个类,其方法注释为@Scheduled @Component @Slf4j public class MyScheduler { @Scheduled(cron = "${polling-job-cron}") //each minute public void pollingJob() { log.info("starting polling job..."); //some work log.info("polling
@Component
@Slf4j
public class MyScheduler {
@Scheduled(cron = "${polling-job-cron}") //each minute
public void pollingJob() {
log.info("starting polling job...");
//some work
log.info("polling job finished.");
}
}
以及taskScheduler的配置:
@Bean
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(5);
scheduler.setThreadNamePrefix("mynameofscheduler");
scheduler.setWaitForTasksToCompleteOnShutdown(true);
scheduler.setAwaitTerminationSeconds(30);
scheduler.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
return scheduler;
}
我试图通过使用等待ContextClosedEvent
的类来使用优雅关机:
@Component
@Slf4j
public class GracefulShutdown implements ApplicationListener<ContextClosedEvent> {
private final ApplicationContext context;
private final ThreadPoolTaskScheduler taskScheduler;
public GracefulShutdown(ApplicationContext context,
ThreadPoolTaskScheduler taskScheduler) {
this.context = context;
this.taskScheduler = taskScheduler;
}
@Override
public void onApplicationEvent(ContextClosedEvent event) {
log.info("Graceful shutdown - start");
log.info("Closing task scheduler");
taskScheduler.shutdown(); //1
taskScheduler.getScheduledThreadPoolExecutor().shutdown(); //2
log.error("Closed task scheduler");
//give k8s a chance to hit in readinessProbe and stop sending requests to this pod
try {
Thread.sleep(80000); //3
} catch (InterruptedException error) {
log.info("error while trying to sleep");
error.printStackTrace();
}
log.info("Closing spring context with startup date, {}, parent: {}, id: {}, name: {}",
context.getStartupDate(), context.getParent(), context.getId(), context.getDisplayName());
((ConfigurableApplicationContext) context).close();
log.info("Graceful shutdown - end");
}
threadPoolPrefix记录在这些行的前面(由于行太长,无法读取,所以我在上面删掉了该行):
我想可能使用了其他taskScheduler,我关闭了一个错误的taskScheduler,但它都是在@Bean
thx中配置的mynameofscheduler
。我搞砸了,弹簧关闭了水流。我已通过注册关机挂钩修复了此问题:
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(AgileStreamApplication.class, args);
Runtime.getRuntime().addShutdownHook(new Thread(new GracefulShutdownHook(context)));
}
现在我不必显式地关闭TaskScheduler。它由spring完成。因为默认情况下,ScheduledThreadPoolExecutor将等待所有延迟的计划任务完成执行,即使计划任务当时没有运行 请尝试以下操作:
@Bean
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler() {
private static final long serialVersionUID = -1L;
@Override
public void destroy() {
this.getScheduledThreadPoolExecutor().setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
super.destroy();
}
};
scheduler.setPoolSize(5);
scheduler.setThreadNamePrefix("mynameofscheduler");
scheduler.setWaitForTasksToCompleteOnShutdown(true);
scheduler.setAwaitTerminationSeconds(30);
scheduler.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
return scheduler;
}
然后ScheduledThreadPoolExecutor将只等待当前正在运行的计划任务完成执行。Spring已经调用了
shutdown
方法,因此无需再次执行。您的代码还将导致一个错误,因为上下文已经关闭,并且您正在再次调用close。将抛出已关闭异常(或类似异常)的AIK。所以我想说,您尝试正常关机实际上是在阻止正常关机。不幸的是,我还没有开始无缘无故地关闭taskScheduler。我注意到它仍然在Thread.sleep(80000)时运行@Scheduled方法(这对于kubernetes来说是个肮脏的把戏)。因此,否-它没有关闭它,至少没有以我期望的方式关闭:)+我没有收到您提到的错误。它关闭是因为ThreadPoolTaskScheduler
实现了DisposableBean
,并且在关闭/销毁上下文时调用了Distroy
方法。因此,是的,它是关闭的,但关闭不会阻止它接受其他任务(这是您在日志中看到的),再次关闭它(一次又一次)不会改变这一点。另外,您确定这是您的@Scheduled
使用的任务调度程序吗?是的,此任务调度程序作为其threadNamePrefix记录在与“starting polling job.”相同的行上(我已经将其删除,因为行很长,但将编辑/添加到问题中),然后它还没有关闭,这很奇怪(显然你的“修复”也没有帮助)。是否(出于某种原因)有多个实例?你能创建一个复制实例吗?
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(AgileStreamApplication.class, args);
Runtime.getRuntime().addShutdownHook(new Thread(new GracefulShutdownHook(context)));
}
@Bean
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler() {
private static final long serialVersionUID = -1L;
@Override
public void destroy() {
this.getScheduledThreadPoolExecutor().setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
super.destroy();
}
};
scheduler.setPoolSize(5);
scheduler.setThreadNamePrefix("mynameofscheduler");
scheduler.setWaitForTasksToCompleteOnShutdown(true);
scheduler.setAwaitTerminationSeconds(30);
scheduler.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
return scheduler;
}