1. Overview
1.概述
On shutdown, by default, Spring’s TaskExecutor simply interrupts all running tasks, but it may be nice to instead have it wait for all running tasks to be complete. This gives a chance for each task to take measures to ensure the shutdown is safe.
在关闭时,默认情况下,Spring的TaskExecutor会简单地中断所有正在运行的任务,但如果让它等待所有正在运行的任务完成,可能会更好。这给了每个任务一个机会来采取措施以确保关机的安全。
In this quick tutorial, we’ll learn how to do this more graceful shutdown of a Spring Boot application when it involves tasks executing using thread pools.
在这个快速教程中,我们将学习如何在Spring Boot应用程序涉及使用线程池执行的任务时进行这种更优雅的关闭。
2. Simple Example
2.简单的例子
Let’s consider a simple Spring Boot application. We’ll autowire the default TaskExecutor bean:
让我们考虑一个简单的Spring Boot应用程序。我们将自动连接默认的TaskExecutorbean。
@Autowired
private TaskExecutor taskExecutor;
On application startup, let’s execute a 1-minute-long process using a thread from the thread pool:
在应用程序启动时,让我们使用线程池中的一个线程执行一个1分钟的进程。
taskExecutor.execute(() -> {
Thread.sleep(60_000);
});
When a shutdown is initiated, for example, 20 seconds after startup, the thread in the example is interrupted and the application shuts down immediately.
当关机启动时,例如启动后20秒,例子中的线程会被中断,应用程序会立即关闭。
3. Wait for Tasks to Complete
3.等待任务完成
Let’s change the default behavior of task executor by creating a custom ThreadPoolTaskExecutor bean.
让我们通过创建一个自定义的ThreadPoolTaskExecutor bean来改变任务执行器的默认行为。
This class provides a flag setWaitForTasksToCompleteOnShutdown to prevent interrupting running tasks. Let’s set it to true:
这个类提供了一个标志setWaitForTasksToCompleteOnShutdown来防止中断正在运行的任务。让我们把它设置为true。
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(2);
taskExecutor.setMaxPoolSize(2);
taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
taskExecutor.initialize();
return taskExecutor;
}
And, we’ll rewrite the earlier logic to create 3 threads each executing a 1-minute-long task.
而且,我们将重写先前的逻辑,创建3个线程,每个线程执行一个1分钟的任务。
@PostConstruct
public void runTaskOnStartup() {
for (int i = 0; i < 3; i++) {
taskExecutor.execute(() -> {
Thread.sleep(60_000);
});
}
}
Let’s now initiate a shutdown within the first 60 seconds after the startup.
现在让我们在启动后的前60秒内启动关机。
We see that the application shuts down only 120 seconds after startup. The pool size of 2 allows only two simultaneous tasks to execute so the third one is queued up.
我们看到,该应用程序在启动后仅120秒就关闭了。池的大小为2,只允许两个任务同时执行,所以第三个任务被排在了后面。
Setting the flag ensures that both the currently executing tasks and queued up tasks are completed.
设置该标志可以确保当前执行的任务和排队的任务都完成。
Note that when a shutdown request is received, the task executor closes the queue so that new tasks can’t be added.
请注意,当收到关闭请求时,任务执行器会关闭队列 因此无法添加新任务。
4. Max Wait Time Before Termination
4.终止前的最大等待时间
Though we’ve configured to wait for ongoing and queued up tasks to complete, Spring continues with the shutdown of the rest of the container. This could release resources needed by our task executor and cause the tasks to fail.
虽然我们已经配置了等待正在进行的和排队的任务完成,但Spring继续关闭容器的其他部分。这可能会释放我们的任务执行器所需的资源,导致任务失败。
In order to block the shutdown of the rest of the container, we can specify a max wait time on the ThreadPoolTaskExecutor:
为了阻止容器其他部分的关闭,我们可以在ThreadPoolTaskExecutor上指定一个最大等待时间。
taskExecutor.setAwaitTerminationSeconds(30);
This ensures that for the specified time period, the shutdown process at the container level will be blocked.
这可以确保在指定的时间段内,容器级别的关闭过程将被阻止。
When we set the setWaitForTasksToCompleteOnShutdown flag to true, we need to specify a significantly higher timeout so that all remaining tasks in the queue are also executed.
当我们将setWaitForTasksToCompleteOnShutdown标志设置为true时,我们需要指定一个明显更高的超时,以便队列中所有剩余的任务也被执行。
5. Conclusion
5.总结
In this quick tutorial, we saw how to safely shut down a Spring Boot application by configuring the task executor bean to complete the running and submitted tasks until the end. This guarantees that all tasks will have the indicated amount of time to complete their work.
在这个快速教程中,我们看到了如何通过配置任务执行器bean来安全地关闭Spring Boot应用程序,以完成正在运行和提交的任务,直到结束。这保证了所有任务都有指定的时间来完成它们的工作。
One obvious side effect is that it can also lead to a longer shutdown phase. Therefore, we need to decide whether or not to use it depending on the nature of the application.
一个明显的副作用是,它也可能导致更长的关机阶段。因此,我们需要根据应用的性质来决定是否使用它。
As always, the examples from this article are available over on GitHub.
一如既往,本文中的例子可以在GitHub上找到。