How to gracefully stop the spring boot service

Keywords: Java Tomcat Docker AWS

Preface

Often the pronoun for "stop service" is violence, regardless of the consequences, because when a forced stop occurs, it doesn't matter if there are any running threads in it.

It happens that recently, because of the auto scalinng in AWS, unknown friends can interpret it as AWS can automatically expand or shrink our servers, which can reduce costs and allow self-google to get a better understanding.

This is a good starting point, but I also found a problem when I actually used it: if the docker was stopped, the one that might be alive inside would be forced to stop. What should I do at this time?

text

according to

  1. The docker stop command actually executes the kill pid command, and SIGNTEMR is used by default if the stop signal is not followed
  2. And if the main process in the docker is stopped, the docker will naturally stop.

So the key to inferring is that we need to manipulate spring boot with elegant stop, our protagonist today.

So much rubbish has to be mentioned and the following topics have to be addressed. There are actually many online tutorials in this area, such as the one below, which is well written:

https://www.cnblogs.com/harrychinese/p/SpringBoot-graceful-shutdown.html

However, almost all online documents place injection bean s in the startup class, and I place them in the @configuration class. Here's the main code:

First is the code that the primary listening container closes and handles:

package com.demo.timeout.tomcat;

import org.apache.catalina.connector.Connector;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.stereotype.Component;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Component
public class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
    private volatile Connector connector;
    private int waitTime;

    @Value("${STOP_WAIT_TIMEOUT}")
    public void setWaitTime(int waitTime) {
        this.waitTime = waitTime;
    }
    @Override
    public void customize(Connector connector) {
        this.connector = connector;
    }

    @Override
    public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
        this.connector.pause();
        Executor executor = this.connector.getProtocolHandler().getExecutor();
        try {
            if (executor instanceof ThreadPoolExecutor) {
                ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
                threadPoolExecutor.shutdown();
                if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) {
                    System.out.println("Tomcat Process in" + waitTime + " Can't end in seconds, try to force end");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            Thread.currentThread().interrupt();
        }
    }
}

Then inject the code into it:

package com.demo.timeout.config;

import com.demo.timeout.tomcat.GracefulShutdown;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class GracefulShutDownConfig {

    @Autowired
    private GracefulShutdown gracefulShutdown;

    @Bean
    public ServletWebServerFactory servletContainer() {
        TomcatServletWebServerFactory tomcatServletWebServerFactory = new TomcatServletWebServerFactory();
        tomcatServletWebServerFactory.addConnectorCustomizers(gracefulShutdown);
        return tomcatServletWebServerFactory;
    }
}

Finally, I wrote a test code for the API

package com.demo.timeout.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @GetMapping("/30s-process")
    public String longProcessAPI(){
        System.out.println("enter this long process function");
        try {
            Thread.sleep(30000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "30s Thread Start";
    }
}

If you need to see the code I uploaded:

https://github.com/luckypoison/SpringBoot-Shutdown-Graceful

Posted by scopley on Sun, 17 Nov 2019 18:35:32 -0800