Graceful Shutdown Spring Boot Applications
Want to find out how to gracefully shutdown your Spring Boot apps? Click here to learn more about shutting down Spring Boot apps and installing newer versions.
Join the DZone community and get the full member experience.
Join For FreeThis guide walks through the process gracefully shutting down a Spring Boot application.
The implementation of this blog post is originally created by Andy Wilkinson and adapted by me to Spring Boot 2. The code is based on this GitHub comment.
Introduction
A lot of developers and architects discuss the application design, traffic load, frameworks, and patterns to apply to code, but very few of them are discussing the shutdown phase.
Let’s consider this scenario — there’s an application that has a long blocking process. Along with that, this app needs to be shut down and replaced with a newer version. Wouldn't it be nice if, instead of killing all the connections, it just gracefully waited to finish before the shutdown?
That’s what we are going to cover in this guide!
Pre-requisites
- JDK 1.8
- A text editor or your favorite IDE
- Maven 3.0+
Spring Boot, Tomcat
To make this feature work, the first step is to implement the TomcatConnectorCustomizer
.
import org.apache.catalina.connector.Connector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
private static final Logger log = LoggerFactory.getLogger(GracefulShutdown.class);
private volatile Connector connector;
@Override
public void customize(Connector connector) {
this.connector = connector;
}
@Override
public void onApplicationEvent(ContextClosedEvent event) {
this.connector.pause();
Executor executor = this.connector.getProtocolHandler().getExecutor();
if (executor instanceof ThreadPoolExecutor) {
try {
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
threadPoolExecutor.shutdown();
if (!threadPoolExecutor.awaitTermination(30, TimeUnit.SECONDS)) {
log.warn("Tomcat thread pool did not shut down gracefully within "
+ "30 seconds. Proceeding with forceful shutdown");
}
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
}
In the implementation shown above, the ThreadPoolExecutor
will be waiting 30
seconds prior to proceeding with the shutdown. Pretty simple, right? With that in place, it’s now time to register this bean to the application context
and inject it to Tomcat
.
To do that, just create the following Spring @Bean
s.
@Bean
public GracefulShutdown gracefulShutdown() {
return new GracefulShutdown();
}
@Bean
public ConfigurableServletWebServerFactory webServerFactory(final GracefulShutdown gracefulShutdown) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.addConnectorCustomizers(gracefulShutdown);
return factory;
}
How to Test?
To test this implementation, I just created a LongProcessController
that which has a Thread.sleep
of 10
seconds.
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class LongProcessController {
@RequestMapping("/long-process")
public String pause() throws InterruptedException {
Thread.sleep(10000);
return "Process finished";
}
}
Now, it’s just a matter of running your Spring Boot application, making a request to the /long-process
endpoint, and, in the meantime, kill it with a SIGTERM
.
Locate the Process ID
When you start the application, you can locate the process ID in the logs. In my case, it’s 6578
.
2018-06-28 20:37:28.292 INFO 6578 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2018-06-28 20:37:28.296 INFO 6578 --- [ main] c.m.wd.gracefulshutdown.Application : Started Application in 2.158 seconds (JVM running for 2.591)
Request and Shutdown
Perform a request with the /long-process
endpoint. I’m using curl
for that:
$ curl -i localhost:8080/long-process
Send a SIGTERM
to the process:
$ kill 6578
The curl
request still needs to respond as below:
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 14
Date: Thu, 28 Jun 2018 18:39:56 GMT
Process finished
Summary
Congratulations! You just learned how to gracefully shutdown Spring Boot apps.
Published at DZone with permission of Marcos Barbero, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments