【问题标题】:Spring Boot scheduled Runnable tasks continue executing even after server is shut down with an actuator即使在使用执行器关闭服务器后,Spring Boot 计划的 Runnable 任务也会继续执行
【发布时间】:2022-10-18 19:58:34
【问题描述】:

我目前正在开发一个基于 Spring 的 Web 平台,该平台利用了几个访问中央数据库的预定进程。我想介绍用于关闭和重启的执行器。但是,我遇到了一个问题:即使关闭请求以 200 响应被接受,应用程序上下文也开始关闭:

2022-10-10 17:37:25.235  INFO 9067 --- [       Thread-3] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2022-10-10 17:37:25.240  INFO 9067 --- [       Thread-3] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

之后,我反复收到以下异常:

org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is java.lang.IllegalStateException: EntityManagerFactory is closed
    at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:467) ~[spring-orm-5.3.16.jar:5.3.16]
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.startTransaction(AbstractPlatformTransactionManager.java:400) ~[spring-tx-5.3.16.jar:5.3.16]
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:373) ~[spring-tx-5.3.16.jar:5.3.16]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:595) ~[spring-tx-5.3.16.jar:5.3.16]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:382) ~[spring-tx-5.3.16.jar:5.3.16]
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.16.jar:5.3.16]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.16.jar:5.3.16]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753) ~[spring-aop-5.3.16.jar:5.3.16]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:698) ~[spring-aop-5.3.16.jar:5.3.16]
    at usi.si.seart.service.TaskService$TaskServiceImpl$$EnhancerBySpringCGLIB$$4bf83a1d.getNext(<generated>) ~[classes/:na]
    at usi.si.seart.scheduling.TaskRunner.run(TaskRunner.java:68) ~[classes/:na]
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) ~[spring-context-5.3.16.jar:5.3.16]
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) ~[na:na]
    at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305) ~[na:na]
    at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
    at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]
Caused by: java.lang.IllegalStateException: EntityManagerFactory is closed
    at org.hibernate.internal.SessionFactoryImpl.validateNotClosed(SessionFactoryImpl.java:547) ~[hibernate-core-5.6.5.Final.jar:5.6.5.Final]
    at org.hibernate.internal.SessionFactoryImpl.createEntityManager(SessionFactoryImpl.java:636) ~[hibernate-core-5.6.5.Final.jar:5.6.5.Final]
    at org.hibernate.internal.SessionFactoryImpl.createEntityManager(SessionFactoryImpl.java:158) ~[hibernate-core-5.6.5.Final.jar:5.6.5.Final]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.createNativeEntityManager(AbstractEntityManagerFactoryBean.java:585) ~[spring-orm-5.3.16.jar:5.3.16]
    at jdk.internal.reflect.GeneratedMethodAccessor112.invoke(Unknown Source) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.invokeProxyMethod(AbstractEntityManagerFactoryBean.java:487) ~[spring-orm-5.3.16.jar:5.3.16]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean$ManagedEntityManagerFactoryInvocationHandler.invoke(AbstractEntityManagerFactoryBean.java:734) ~[spring-orm-5.3.16.jar:5.3.16]
    at com.sun.proxy.$Proxy175.createNativeEntityManager(Unknown Source) ~[na:na]
    at org.springframework.orm.jpa.JpaTransactionManager.createEntityManagerForTransaction(JpaTransactionManager.java:485) ~[spring-orm-5.3.16.jar:5.3.16]
    at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:410) ~[spring-orm-5.3.16.jar:5.3.16]
    ... 17 common frames omitted

因此,尽管服务器被报告为“正在关闭”,但计划的进程仍然继续执行。我在配置类中的调度器定义如下:

@Bean
public ThreadPoolTaskScheduler taskScheduler() {
    ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
    threadPoolTaskScheduler.setClock(Clock.systemUTC());
    threadPoolTaskScheduler.setPoolSize(3);
    threadPoolTaskScheduler.setThreadNamePrefix("PlatformScheduler");
    threadPoolTaskScheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
    threadPoolTaskScheduler.setRemoveOnCancelPolicy(true);
    threadPoolTaskScheduler.initialize();

    threadPoolTaskScheduler.schedule(getRepoMaintainer(), new CronTrigger("0 0 0 * * SUN"));
    threadPoolTaskScheduler.schedule(getTaskCleaner(), new CronTrigger("0 */15 * * * *"));
    threadPoolTaskScheduler.scheduleWithFixedDelay(getTaskRunner(), 500);

    return threadPoolTaskScheduler;
}

其中getRepoMaintainergetTaskCleanergetTaskRunner 都是产生自定义Runnable 实例的方法:分别为RepoMaintainerTaskCleanerTaskRunner

【问题讨论】:

  • 您不必指定任何destroyMethodThreadPoolTaskScheduler 实现DisposableBean 并具有自动调用的destroy 方法。 Destroy 将调用shutdown,因此这不会导致问题。
  • @zapl 我尝试了指定和不指定destroyMethod,问题仍然存在。我将编辑问题以删除注释参数

标签: java spring spring-boot spring-boot-actuator


【解决方案1】:

正如您已指定您的自定义destroyMethod 是“关闭”您是否使用所需的销毁语句定义了该方法?

像这样的东西,

public void shutdown() {
    threadPoolTaskScheduler.shutdown();
}

编辑- 这里的错误很可能是,即使已启动关闭任务仍在运行但容器正在关闭。这会释放任务执行所需的资源并引发错误。 您可以在此处执行正常关闭,您可以在任务完成执行之前阻止容器关闭。

您的问题与one 类似,您可以试试他的solution

或者,您可以像这样配置终止期限,setAwaitTerminationSeconds(*seconds*) 查看更多here。但是你可能会遇到这个issue

【讨论】:

  • 嗨@Jalena。我查看了@BeandestroyMethod 的JavaDoc,据我了解,您提供的方法名称是将在该类中调用的方法的名称。对于ThreadPoolTaskScheduler,我指定了shutdown。由于注释默认执行此方法推断,因此我尝试删除注释值。调试程序我注意到确实调用了shutdown 方法,但同样的问题仍然存在......
  • 嗨@OzrenDabić,我已经编辑了我的原始答案,您可以尝试解决方案。希望能帮助到你!
  • 我将看看第一个解决方案。由于其中一些计划任务通常运行相当长的时间,它们可能会延迟相当长的关闭时间。我希望立即关闭,这就是为什么使用setAwaitTerminationSeconds 是不可能的。
  • @OzrenDabić 在这种情况下,您可以在 doc 中找到一些有用的方法,例如 removeOnCancelPolicy 或 ExecuteExistingDelayedTasksAfterShutdownPolicy。这也解决了here
【解决方案2】:

不幸的是,其他用户建议的修复都没有奏效。最后,我设法通过自定义 ThreadPoolTaskScheduler 实现解决了我的问题:

ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler() {
    private final Set<ScheduledFuture<?>> futures = new HashSet<>();

    @Override
    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay) {
        ScheduledFuture<?> future = super.scheduleWithFixedDelay(task, delay);
        futures.add(future);
        return future;
    }

    @Override
    public ScheduledFuture<?> schedule(Runnable task, Trigger trigger) {
        ScheduledFuture<?> future =  super.schedule(task, trigger);
        futures.add(future);
        return future;
    }

    @Override
    public void shutdown() {
        futures.forEach(future -> future.cancel(true));
        super.shutdown();
    }
};

这样,我确保无论如何在关机前取消所有计划任务。

【讨论】:

    猜你喜欢
    • 2021-12-15
    • 1970-01-01
    • 2018-01-23
    • 2012-07-02
    • 2013-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-11
    相关资源
    最近更新 更多