spring boot 2.0 实现优雅停机

原文链接

前期踩的坑 (spring boot 1.x)

1. 添加mavne依赖

  
  
    org.springframework.boot  
    spring-boot-starter-actuator  
  

2. 启用shutdown

在配置文件里添加下面的配置

#启用shutdown endpoint的HTTP访问
endpoints.shutdown.enabled=true
#不需要验证 
endpoints.shutdown.sensitive=false

启动的时候可以看到下面的日志,就说明成功了


spring boot 2.0 实现优雅停机_第1张图片
启动日志.png

3. 优雅停机

发送POST请求 http://localhost:8080/shutdown
如果响应码是404 可以尝试POST http://localhost:8080/actuator/shutdown

spring boot 2.0

如果你使用的spring boot版本是2.x的就会发现,这些POST请求都会出现404的结果。

下面是spring boot 2.0 优雅停机的实现方式。

1.修改Application启动类

tomcat容器

@SpringBootApplication
public class ShutdownApplication {

    public static void main(String[] args) {
        SpringApplication.run(ShutdownApplication.class, args);
    }

    /**
     * 用于接受 shutdown 事件
     */
    @Bean
    public GracefulShutdown gracefulShutdown() {
        return new GracefulShutdown();
    }

    /**
     * 配置tomcat
     *
     * @return
     */
    @Bean
    public ServletWebServerFactory servletContainer() {
        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
        tomcat.addConnectorCustomizers(gracefulShutdown());
        return tomcat;
    }

    /**
     * 优雅关闭 Spring Boot。容器必须是 tomcat
     */
    private class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener {
        private final Logger log = LoggerFactory.getLogger(GracefulShutdown.class);
        private volatile Connector connector;
        private final int waitTime = 10;

        @Override
        public void customize(Connector connector) {
            this.connector = connector;
        }

        @Override
        public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
            this.connector.pause();
            Executor executor = this.connector.getProtocolHandler().getExecutor();
            if (executor instanceof ThreadPoolExecutor) {
                try {
                    ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
                    threadPoolExecutor.shutdown();
                    if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) {
                        log.warn("Tomcat 进程在" + waitTime + " 秒内无法结束,尝试强制结束");
                    }
                } catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }
}

Undertow容器 (没有使用过,不保证可用)

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    /**
     * 优雅关闭 Spring Boot
     */
    @Component
    public class GracefulShutdown implements ApplicationListener {

        @Autowired
        private GracefulShutdownWrapper gracefulShutdownWrapper;

        @Autowired
        private ServletWebServerApplicationContext context;

        @Override
        public void onApplicationEvent(ContextClosedEvent contextClosedEvent){
            gracefulShutdownWrapper.getGracefulShutdownHandler().shutdown();
            try {
                UndertowServletWebServer webServer = (UndertowServletWebServer)context.getWebServer();
                Field field = webServer.getClass().getDeclaredField("undertow");
                field.setAccessible(true);
                Undertow undertow = (Undertow) field.get(webServer);
                List listenerInfo = undertow.getListenerInfo();
                Undertow.ListenerInfo listener = listenerInfo.get(0);
                ConnectorStatistics connectorStatistics = listener.getConnectorStatistics();
                while (connectorStatistics.getActiveConnections() > 0){}
            }catch (Exception e){
                // Application Shutdown
            }
        }
    }
    @Component
    public class GracefulShutdownWrapper implements HandlerWrapper{

        private GracefulShutdownHandler gracefulShutdownHandler;

        @Override
        public HttpHandler wrap(HttpHandler handler) {
            if(gracefulShutdownHandler == null) {
                this.gracefulShutdownHandler = new GracefulShutdownHandler(handler);
            }
            return gracefulShutdownHandler;
        }

        public GracefulShutdownHandler getGracefulShutdownHandler() {
            return gracefulShutdownHandler;
        }

    }
    @Component
    @AllArgsConstructor
    public class UndertowExtraConfiguration {

        private final GracefulShutdownWrapper gracefulShutdownWrapper;

        @Bean
        public UndertowServletWebServerFactory servletWebServerFactory() {
            UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
            factory.addDeploymentInfoCustomizers(deploymentInfo -> deploymentInfo.addOuterHandlerChainWrapper(gracefulShutdownWrapper));
            factory.addBuilderCustomizers(builder -> builder.setServerOption(UndertowOptions.ENABLE_STATISTICS, true));
            return factory;
        }

    }
}

2. 使用 kill命令杀死进程

使用下面的命令杀死进程。该命令是向 某个进程发送终止信号。

kill -15 [PID]

你可能感兴趣的:(spring boot 2.0 实现优雅停机)