introduction
Start and stop of an application is a common operation. However, abruptly terminating a running application may result in failure of the request being processed, inconsistent data, and other problems. Graceful Shutdown refers to the process in which an application can properly handle existing requests, release resources, and then exit after receiving the stop signal. This article will introduce in detail three ways to achieve elegant downtime in SpringBoot.
What is elegant downtime?
Elegant downtime means that the application does not terminate immediately after receiving the stop signal, but rather exits in an orderly manner following the following steps:
- Stop receiving new requests
- Wait for the request being processed to complete
- Close various resource connections (database connection pool, thread pool, message queue connection, etc.)
- Complete the necessary cleaning work
- Finally exit the application
The core value of elegant downtime is:
- Improve user experience and avoid sudden interruptions in requests
- Ensure data consistency and prevent data loss
Method 1: SpringBoot built-in elegant downtime support
Principles and Supported Versions
Starting with Spring Boot 2.3, the framework natively supports elegant downtime mechanisms. This is the easiest and officially recommended way to implement it.
When an application receives a stop signal (such as SIGTERM), the embedded web server (such as Tomcat, Jetty, or Undertow) performs the following steps:
- Stop receiving new connection requests
- Set keepalive of existing connection to false
- Wait for all active request processing to complete or timeout
- Close application context and related resources
Configuration method
existor
Add simple configuration to enable:
server: shutdown: graceful spring: lifecycle: timeout-per-shutdown-phase: 30s
Heretimeout-per-shutdown-phase
Specifies the maximum time to wait for the active request to complete, the default is 30 seconds.
Implementation example
Here is a complete SpringBoot application example with elegant downtime enabled:
@SpringBootApplication public class GracefulShutdownApplication { private static final Logger logger = (); public static void main(String[] args) { (, args); ("Application started"); } @RestController @RequestMapping("/api") static class SampleController { @GetMapping("/quick") public String quickRequest() { return "Quick response"; } @GetMapping("/slow") public String slowRequest() throws InterruptedException { // Simulate long-term processing requests ("Start processing slow request"); (10000); // 10 seconds ("Finished processing slow request"); return "Slow response completed"; } } @Bean public ApplicationListener<ContextClosedEvent> contextClosedEventListener() { return event -> ("Received spring context closed event - shutting down"); } }
Test verification
- Start the application
- Initiate a long-running request:
curl http://localhost:8080/api/slow
- During processing, send SIGTERM signal to the application:
kill -15 <Process ID>
, If it is an IDEA development environment, you can click the Stop Service button once - Observe log output: You should see that the application is waiting for the long request processing to be completed before closing
Pros and cons
advantage:
- Simple configuration, official native support
- No extra code required, low maintenance cost
- Suitable for most web application scenarios
- Perfect integration with Spring lifecycle events
shortcoming:
- Only support Spring Boot 2.3+ version
- Additional processing is required for scenarios that exceed HTTP requests (such as long-running jobs)
- Relatively low flexibility, no fine control of downtime
- Only a unified timeout time can be set
Applicable scenarios
- Spring Boot 2.3+ version of web application
- The request processing time is expected, and there will be no requests running for a long time.
- Standard services in microservice architecture
Method 2: Use Actuator endpoint to achieve elegant shutdown
Principles and Implementation
Spring Boot Actuator provides a rich operation and maintenance endpoint, includingshutdown
Endpoint, which can be used to trigger an elegant downtime of the application. This approach is unique in that it allows the downtime process to be triggered via HTTP requests, suitable for scenarios where remote operations are required.
Configuration steps
- Add Actuator dependencies:
<dependency> <groupId></groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
- Enable and expose shutdown endpoint:
management: endpoint: shutdown: enabled: true endpoints: web: exposure: include: shutdown base-path: /management server: port: 8081 # Optional: Set a separate port for the management endpoint
How to use
Triggering downtime via HTTP POST request:
curl -X POST http://localhost:8081/management/shutdown
After the request is successful, a response like the following will be returned:
{ "message": "Shutting down, bye..." }
Safety considerations
Since shutdown is a sensitive operation, security must be considered:
spring: security: user: name: admin password: secure_password roles: ACTUATOR management: endpoints: web: exposure: include: shutdown endpoint: shutdown: enabled: true # Configure endpoint security-path: /management
Access methods after using security configuration:
curl -X POST http://admin:secure_password@localhost:8080/management/shutdown
Complete implementation example
@SpringBootApplication @EnableWebSecurity public class ActuatorShutdownApplication { private static final Logger logger = (); public static void main(String[] args) { (, args); ("Application started with Actuator shutdown enabled"); } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { ().disable() .authorizeHttpRequests() .requestMatchers("/management/**").hasRole("ACTUATOR") .anyRequest().permitAll() .and() .httpBasic(); return (); } @RestController static class ApiController { @GetMapping("/api/hello") public String hello() { return "Hello, world!"; } } @Bean public ApplicationListener<ContextClosedEvent> shutdownListener() { return event -> { ("Received shutdown signal via Actuator"); // Wait for the active request to complete ("Waiting for active requests to complete..."); try { (5000); // Simplify the example, the active request should be monitored in fact } catch (InterruptedException e) { ().interrupt(); } ("All requests completed, shutting down"); }; } }
Pros and cons
advantage:
- Can remotely trigger shutdown via HTTP request
- Suitable for management tools and operation and maintenance script integration
- Access control can be achieved in integration with Spring Security
- Supports all Spring Boot versions (including versions before 2.3)
shortcoming:
- Requires additional configuration and dependencies
- Potential security risks require careful protection of endpoints
- Additional encoding is required for the shutdown of internal complex resources
Applicable scenarios
- Scenarios where downtime is required through HTTP request
- Manage application deployment using operation and maintenance automation tools
- Services need to be stopped in a specific order in a cluster environment
- Internal management system needs to directly control the application life cycle
Method 3: Custom ShutdownHook to achieve elegant shutdown
Principles and Implementation
The JVM provides a ShutdownHook mechanism that allows custom logic to be executed before the JVM is closed. By registering a custom ShutdownHook, we can achieve more granular and flexible elegant downtime control. The advantage of this method is that it can accurately control the resource release order and is suitable for applications with complex resource management needs.
Basic implementation steps
- Create a custom ShutdownHandler class
- Register JVM ShutdownHook
- Implement custom elegant shutdown logic in Hook
Complete implementation example
Here is a complete example with detailed comments:
@SpringBootApplication public class CustomShutdownHookApplication { private static final Logger logger = (); public static void main(String[] args) { ConfigurableApplicationContext context = (, args); // Register a custom ShutdownHook registerShutdownHook(context); ("Application started with custom shutdown hook"); } private static void registerShutdownHook(ConfigurableApplicationContext context) { ().addShutdownHook(new Thread(() -> { ("Executing custom shutdown hook"); try { // 1. Stop receiving new requests (if it is a web application) if (("tomcatServletWebServerFactory")) { TomcatServletWebServerFactory server = (); ("Stopping web server to reject new requests"); // Note: In actual applications, you need to find the correct way to stop a specific web server } // 2. Wait for the active request processing to complete ("Waiting for active requests to complete"); // Custom waiting logic can be added here, such as checking the number of active connections or thread status (5000); // Simplify the example // 3. Close the custom thread pool shutdownThreadPools(context); // 4. Close the message queue connection closeMessageQueueConnections(context); // 5. Close the database connection pool closeDataSourceConnections(context); // 6. Execute other custom cleanup logic performCustomCleanup(context); // 7. Finally close Spring context ("Closing Spring application context"); (); ("Graceful shutdown completed"); } catch (Exception e) { ("Error during graceful shutdown", e); } }, "GracefulShutdownHook")); } private static void shutdownThreadPools(ApplicationContext context) { ("Shutting down thread pools"); // Get all ExecutorService types beans Map<String, ExecutorService> executors = (); ((name, executor) -> { ("Shutting down executor: {}", name); (); try { // Wait for the task to be completed if (!(15, )) { ("Executor did not terminate in time, forcing shutdown: {}", name); (); } } catch (InterruptedException e) { ().interrupt(); ("Interrupted while waiting for executor shutdown: {}", name); (); } }); } private static void closeMessageQueueConnections(ApplicationContext context) { ("Closing message queue connections"); // Example: Close RabbitMQ connection if (("rabbitConnectionFactory")) { try { ConnectionFactory rabbitFactory = (); // Close the connection appropriately ("Closed RabbitMQ connections"); } catch (Exception e) { ("Error closing RabbitMQ connections", e); } } // Example: Close Kafka connection if (("kafkaConsumerFactory")) { try { // Close the code for Kafka connection ("Closed Kafka connections"); } catch (Exception e) { ("Error closing Kafka connections", e); } } } private static void closeDataSourceConnections(ApplicationContext context) { ("Closing datasource connections"); // Get all DataSource types beans Map<String, DataSource> dataSources = (); ((name, dataSource) -> { try { // For HikariCP connection pool if (dataSource instanceof HikariDataSource) { ((HikariDataSource) dataSource).close(); ("Closed HikariCP datasource: {}", name); } // You can add shutdown logic for other types of connection pools else { // Try to call the close method through reflection Method closeMethod = ().getMethod("close"); (dataSource); ("Closed datasource: {}", name); } } catch (Exception e) { ("Error closing datasource: {}", name, e); } }); } private static void performCustomCleanup(ApplicationContext context) { // Here you can add application-specific cleaning logic ("Performing custom cleanup tasks"); // For example: save application status // For example: Release local resources // For example: Send a shutdown notification to other systems } @Bean public ExecutorService applicationTaskExecutor() { return (10); } @RestController @RequestMapping("/api") static class ApiController { @Autowired private ExecutorService applicationTaskExecutor; @GetMapping("/task") public String submitTask() { (() -> { try { ("Task started, will run for 30 seconds"); (30000); ("Task completed"); } catch (InterruptedException e) { ("Task interrupted"); ().interrupt(); } }); return "Task submitted"; } } }
Pros and cons
advantage:
- Maximum flexibility and customizability
- You can accurately control the order and method of resource closure
- Suitable for complex application scenarios and all Spring Boot versions
- Can handle external resources that Spring framework cannot manage
shortcoming:
- High complexity of implementation, need to know the application resources in detail
- High maintenance costs
- Prone to problems caused by resource closure order errors
Applicable scenarios
- Applications with complex resource management requirements
- Scenarios where resources need to be closed in a specific order
- Using Spring Boot earlier versions (built-in elegant downtime is not supported)
- Non-Web application or hybrid application architecture
- Resources that are not directly managed by Spring framework (such as Native resources)
Program comparison and selection guide
The following is a comparison table of three solutions to help you choose the implementation method that best suits your scenario:
Features/Scheme | SpringBoot built-in | Actuator endpoint | Custom ShutdownHook |
---|---|---|---|
Implement complexity | Low | middle | high |
flexibility | Low | middle | high |
Customizable | Low | middle | high |
Framework dependency | Spring Boot 2.3+ | Any Spring Boot version | Any Java application |
Extra dependencies | none | Actuator | none |
Trigger method | System Signal (SIGTERM) | HTTP Request | System signals or customizations |
Safety considerations | Low | High (need to protect endpoints) | middle |
Maintenance cost | Low | middle | high |
Applicable to web applications | Best for | Suitable | Suitable |
Applicable to non-web applications | Some suitable | Some suitable | Best for |
-
Choose SpringBoot built-in solution,if:
- Using Spring Boot Version 2.3+
- Mainly standard web applications
- No special resource management requirements
- Hope the easiest configuration
-
Select Actuator endpoint scheme,if:
- The shutdown needs to be triggered via HTTP request
- Using earlier Spring Boot versions
- Integrated operation and maintenance automation tools
- Already monitoring using Actuator
-
Select a custom ShutdownHook solution,if:
- Have complex resource management requirements
- Precise control of downtime processes is required
- Resources that are not directly managed by Spring framework
- Hybrid architecture applications (both web and background jobs)
in conclusion
Elegant downtime is an important practice to ensure application reliability and user experience. SpringBoot provides a variety of implementation methods, from simple configuration to complex custom implementations, which can meet the needs of different application scenarios.
- For simple applications: SpringBoot built-in solution is the best choice, with simple configuration, enough to meet the needs of most web applications
- For scenarios that require remote triggering: Actuator endpoint provides HTTP interface control, which facilitates integrated operation and maintenance system
- For complex applications: Custom ShutdownHook provides maximum flexibility to accurately control the order and way of resource release
Elegant downtime should be a standard practice in microservice design, no matter which method you choose. Correctly achieving elegant downtime can not only improve system stability, but also improve user experience and reduce business interruptions caused by application restarts or downgrades.
The above is the detailed content of the three ways to achieve elegant shutdown in SpringBoot. For more information about SpringBoot's elegant shutdown, please pay attention to my other related articles!