Graceful shutdown of different versions of spring boot and configuration of running in winsw service mode under windows

cause

  • spring boot will not shut down gracefully by default, which will interrupt the running program when restarting, resulting in failure.

Current solution

  • Spring boot starter actor monitoring class library is introduced. One of its functions supports elegant shutdown.
  • Since spring boot version 2.3, it has integrated elegant shutdown, which can be realized without introducing the above class library.

Where the pit father

  • The spring boot starter actuator document says that elegant shutdown is supported, but it is only at the spring level and is not linked to containers such as tomcat. It can not be realized until spring boot 2.3 turns on its own elegant shutdown, that is, the version before 2.3 can not realize elegant shutdown at all. You need to further process it according to the containers used. Refer to this issue: Allow the embedded web server to be shut down gracefully.
  • Under linux, sending the command kill -2 xxx can trigger graceful shutdown, but under windows, only ctrl+c can be triggered (you can simulate it with the exit button in run under idea), but under windows, we generally run with services, so ctrl+c can never be triggered. Therefore, even after Version 2.3, Spring boot starter actuator must also be installed under windows to realize graceful shutdown by sending http request.

2.3 or above (9.0.33 or above is required if tomcat container is used)

  • Add in application.yml:
    # Open and close gracefully
    server:
      shutdown: graceful
    # Configure the forced end time. If it is not configured, it defaults to 30s
    spring:
      lifecycle:
        timeout-per-shutdown-phase: 30s
    
  • After configuration, graceful shutdown is supported. The linux side only needs to set the shutdown command in the systemctl configuration file to kill - 2 xxpid. pid can find something according to the port through the file or command. There is no linux to test at hand. If there is a complete script in the later stage.
  • Although the windows side also supports it, it can't trigger the same effect as ctrl+c if running in service mode, so it still can't.

Processing methods below 2.3 (or windows terminal above 2.3)

  • maven class library with spring boot starter actor
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    
  • Add in application.yml:
    #Monitoring related configuration
    management:
      endpoint:
        # open
        shutdown:
          enabled: true
      endpoints:
        web:
          # Only shutdown is allowed. For security, others want to monitor and configure themselves
          exposure:
            include: "shutdown"
          # Custom request path for security
          base-path: /xxx
      server:
        #Custom request port for security
        port: 7080
    
    The path to send the request is as follows: curl -X POST http://localhost: Custom port / custom path / shutdown. Since the path and port are custom, you don't have to worry too much about security.
  • Branch 1: if it is a windows terminal above version 2.3, turn on the built-in graceful shutdown, and you can implement it through http request
    # Open and close gracefully
    server:
      shutdown: graceful
    # Configure the forced end time. If it is not configured, it defaults to 30s
    spring:
      lifecycle:
        timeout-per-shutdown-phase: 30s
    
  • Branch 2: if the version is below 2.3, you can send an http request to close it, but it will not wait for the executing program, but will close it directly. You should also configure the container. Take the tomcat container as an example:
    • Create related classes:
        package xxx.xxx.xxx;
      
        import lombok.extern.slf4j.Slf4j;
        import org.apache.catalina.connector.Connector;
        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;
      
        /**
         * Elegant close
         */
        @Slf4j
        public class GracefulShutdown implements TomcatConnectorCustomizer,
                ApplicationListener<ContextClosedEvent> {
      
            private volatile Connector connector;
      
            /**
             * 15s Force close
             */
            private static final int TIMEOUT = 15;
      
            /**
             * Custom link
             *
             * @param connector
             */
            @Override
            public void customize(Connector connector) {
                this.connector = connector;
            }
      
            /**
             * Triggered when closed
             *
             * @param event
             */
            @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(TIMEOUT, 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();
                    }
                }
            }
        }
      
      • Introduce in startup class:
        package xx.xxx.xxx;
      
        import org.springframework.beans.factory.annotation.Qualifier;
        import org.springframework.boot.SpringApplication;
        import org.springframework.boot.autoconfigure.SpringBootApplication;
        import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
        import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
        import org.springframework.context.annotation.Bean;
      
        @SpringBootApplication
        public class XxxxApplication {
      
            /**
             * Graceful close bean
             * @return
             */
            @Bean("gracefulShutdown")
            public GracefulShutdown gracefulShutdown() {
                return new GracefulShutdown();
            }
      
            /**
             * tomcat Configure graceful shutdown
             * @param gracefulShutdown
             * @return
             */
            @Bean
            public ConfigurableServletWebServerFactory webServerFactory(@Qualifier("gracefulShutdown") GracefulShutdown gracefulShutdown) {
                TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
                factory.addConnectorCustomizers(gracefulShutdown);
                return factory;
            }
      
            public static void main(String[] args) {
                SpringApplication.run(XxxxApplication .class, args);
            }
        }
      
    • After the above configuration, the http request will be closed gracefully, but it is only applicable to the tomcat container. The undertow container can refer to this: Elegant downtime of Spring boot 2.0

windows client uses winsw to set the configuration of service runtime

  • winsw can be downloaded here: winsw , the main function is to make the program run in the background as a service and start up
  • You need to download curl under windows, address: curl for Windows
  • Configure the config file of winsw:
    <service>
      <!-- ID of the service. It should be unique across the Windows system-->
      <id>XXX</id>
      <!-- Display name of the service -->
      <name>xxx</name>
      <!-- Service description -->
      <description>xxx name(powered by WinSW)</description>
      
      <!-- Path to the executable, which should be started -->
      <env name="JAVA_HOME" value="%JAVA_HOME%"/>
      <executable>java</executable>
      <startarguments>-jar -Xms128m -Xmx512m "D:\jar Package path\xxx.jar"</startarguments>
      <!--stop it -->
      <stopexecutable>D:\curl route\bin\curl.exe</stopexecutable>
      <stoparguments>-X POST http://localhost:7080/xxx/shutdown</stoparguments>
      <!--If not configured, the default value is 15 s-->
      <stoptimeout>30 sec</stoptimeout>
      <startmode>Automatic</startmode>
      <logmode>none</logmode>
    </service>
    

end

  • above.

Tags: Java Spring Boot

Posted on Tue, 09 Nov 2021 06:21:48 -0500 by SamLiu