Skip to content

Latest commit

 

History

History
106 lines (75 loc) · 4.48 KB

File metadata and controls

106 lines (75 loc) · 4.48 KB

如何优雅关闭服务

借助 spring-boot

开启优雅关闭终端:endpoints.shutdown.enabled: true

调用接口: POST /shutdown

用docker实现

  • docker stop/restart [-t seconds] [container_id] 发送中断信号,不会接收新的请求,之前的请求会继续运行

    • 新请求无法创建连接:因为服务不再接收新的请求,所以会报503:Service Unavailable, 建议Client设置一个合适的 连接超时时间

    • 超时时间[-t]:到达超时时间后,服务仍会立即中断,所以最好 设置一个合适的超时时间

    • 日志:接收到中断信号后,log不会再打印,但 log.isXXXEnabled() 仍返回 true! 所以不要用log作为判断请求中断的标记,而是用System.out.print

代码解决

该方法只是手动关闭服务线程,前几种方法已经可以做到,不建议使用

  • 实现 自定义 GracefulShutdownConfiguration 通过 @ImportAutoConfiguration(GracefulShutdownConfiguration.class) 启用配置

  • 问题:停止后服务仍未关闭,用jstack看到仍然有线程在执行(基于我当前的服务):

    • org.springframework.cloud.consul.config.ConfigWatch 解决:spring.cloud.consul.config.watch.enabled: false 但是有些服务是需要开启的 查看源码,该线程默认要等待55s来判断结束状态,时间较长

    • ch.qos.logback.classic.AsyncAppender 解决:logback-spring 里加上 <shutdownHook class="ch.qos.logback.core.hook.DelayingShutdownHook"/>``ShutdownHookBase#close 里会调用 Context.stop() 进行关闭

GracefulShutdownConfiguration

@Slf4j
public class GracefulShutdownConfiguration {

    @Bean
    public GracefulShutdown gracefulShutdown() {
        return new GracefulShutdown();
    }

    @Bean
    public TomcatEmbeddedServletContainerFactory webServerFactory(final GracefulShutdown gracefulShutdown) {
        TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
        factory.addConnectorCustomizers(gracefulShutdown);
        return factory;
    }

    class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {

        private volatile Connector connector;

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

        @Override
        public void onApplicationEvent(ContextClosedEvent event) {
            log.info("signal: shutdown");
            log.info(event.toString());
            this.connector.pause();
            Executor executor = this.connector.getProtocolHandler().getExecutor();
            if (executor instanceof ThreadPoolExecutor) {
                try {
                    ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
                    threadPoolExecutor.shutdown();
                    log.info(threadPoolExecutor.getActiveCount() + "");
                    if (!threadPoolExecutor.awaitTermination(30, 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();
                }

            }
        }
    }
}

参考