美文网首页spring boot 学习笔记
spring boot 2.0 实现优雅停机

spring boot 2.0 实现优雅停机

作者: U_2647 | 来源:发表于2018-05-04 14:21 被阅读0次

    原文链接

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

    1. 添加mavne依赖

    <!-- springboot监控 -->  
    <dependency>  
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-starter-actuator</artifactId>  
    </dependency>  
    

    2. 启用shutdown

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

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

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


    启动日志.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<ContextClosedEvent> {
            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<ContextClosedEvent> {
    
            @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<Undertow.ListenerInfo> 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 实现优雅停机

        本文链接:https://www.haomeiwen.com/subject/qcudrftx.html