使用Spring和长时间运行的线程实现正常关机

5ssjco0h  于 2022-12-02  发布在  Spring
关注(0)|答案(1)|浏览(156)

我们遇到的问题是,有多个异步线程处理大量数据,需要几分钟甚至几个小时才能结束。
在这种情况下,我们希望线程在短期内完成它们的工作,并留下一个状态,我们可以使用它来处理剩余的数据。
我们已经使用了graceful.shutdown=enable
现在的想法是,我们在创建异步线程的bean中引入一个带有@PreDestroy的方法。当这个方法被调用时,它将在另一个bean中设置一个“shutdown”标志。所有长时间运行的线程在处理过程中都会检查这个标志,当它为真时将停止处理,并将它们处理的干净状态写入数据库。
这或多或少是有效的...但不是所有的时间。
正如我在第一次得到shutdown触发器后所理解的那样,有一个可配置的时间spring.lifecycle.timeout-per-shutdown-phase,在这个时间内线程可以继续处理它们的工作,而不会减少任何所需的资源。在这个时间过后,所有的shutdownhook都会以未知的顺序处理。这让我想到,我可能没有离开干净状态所必需的所有资源。当我使用@preDestroy和标志的方法时。
有没有更好的解决办法?
是否需要向线程(如setAwaitTerminationSeconds(60);setWaitForTasksToCompleteOnShutdown(true);)添加更多配置?

ru9i0ody

ru9i0ody1#

我发现,当在Spring中使用graceful.shutdown=enable@PreDestroy时,需要对synchronizerun()方法和@PreDestroy方法(前提是这两个方法在同一个类中):

@Service
public class MyClass implements Runnable {
  private volatile boolean shutDown = false;
  public MyClass( TaskScheduler taskScheduler) {
        taskScheduler.scheduleWithFixedDelay(this, 100);
  }
  @PreDestroy
    public synchronized void preDestroy() {
        this.shutDown = true;
        log.info("@PreDestroy Callback triggered for shutting down");
    }
 @Override
    public synchronized void run() {
        if (this.shutDown) {
            this.shutdownCounter++;
            return;
        }
        // put in here, whatever you long running task needs to do
    }

你需要确保你的run()方法没有做任何事情,当preDestroy()已经被调用时,因此shutdown-标志。
synchronize的作用是,preDestroy()会自动等待,直到run()定期结束。只有在preDestroy()结束时,preDestroy()才会被调用。通过设置shutDown标志,它可以防止启动另一个长时间运行的线程,即使run()再次被调用。相反,run()会立即返回。
因为否则的话,当你不同步的时候,在spring服务器得到一个shutdown信号之后,preDestroy()会被立即调用,但是spring thinks如果preDestroy()已经运行并且成功完成,它会立即关闭自己。这不是你想要的,因为这样所有的run()线程都会被中断。
我还发现,当使用@Scheduled-注解(而不是在构造函数中调用taskScheduler.scheduleWithFixedDelay(this, 100))时,上面的例子工作。

相关问题