spring 关闭TaskScheduler不会停止运行@Scheduled方法

wgx48brx  于 2023-08-02  发布在  Spring
关注(0)|答案(2)|浏览(190)

我有一个类,它有一个注解为@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 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");
    }


即使我关闭了taskScheduler和底层的taskExecutor,新任务仍然由@Scheduled运行。发送SIGTERM时运行GracefulShutdown的代码,关闭taskScheduler时运行正常。

Graceful shutdown - start
Closing task scheduler
Closed task scheduler
starting polling job...
polling job finished
starting polling job...
polling job finished.


threadPoolPrefix被记录在这些行的前面(我已经把上面的行删掉了,因为行太长了,读不懂):

{"timeMillis":1534234560001,"thread":"mynameofscheduler","level":"INFO","loggerName":"myclassr","message":"starting polling job..."


我以为可能使用了其他taskScheduler,但我关闭了错误的taskScheduler,但它都是在@Bean中配置的mynameofscheduler

voj3qocg

voj3qocg1#

因为默认情况下,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将只等待当前正在运行的计划任务完成执行。

hujrc8aj

hujrc8aj2#

thx到M. Deinum。我把Spring关流搞砸了。我已经通过注册shutdown hook解决了这个问题:

public static void main(String[] args) {
    ConfigurableApplicationContext context = SpringApplication.run(AgileStreamApplication.class, args);
    Runtime.getRuntime().addShutdownHook(new Thread(new GracefulShutdownHook(context)));
}

字符串
现在我不必显式地关闭taskSchedulers。Spring就能完成。

相关问题