为了账号安全,请及时绑定邮箱和手机立即绑定

Spring Boot 内嵌容器 Tomcat / Undertow / Jetty 优雅停机实现

标签:
SpringBoot

webp

Spring Boot 在关闭时,如果有请求没有响应完,在不同的容器会出现不同的结果,例如,在 Tomcat 和 Undertow 中会出现中断异常,那么就有可能对业务造成影响。所以,优雅停机非常有必要性,目前官方是没有提供很好的策略来实现。

Each SpringApplication registers a shutdown hook with the JVM to ensure that the ApplicationContext closes gracefully on exit. All the standard Spring lifecycle callbacks (such as the DisposableBean interface or the @PreDestroy annotation) can be used.

Spring Boot Application 在接收到停机信号后,可以通过  DisposableBean 接口 、 @PreDestroy 注解 或者 ContextClosedEvent 事件来处理优雅停机的相关逻辑。

版本信息

Spring Boot 版本: 2.0.0.RELEASE

Tomcat 优雅停机

@SpringBootApplicationpublic class Application {    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }    /**
     * 用于接受 shutdown 事件
     */
    @Bean
    public GracefulShutdown gracefulShutdown() {        return new GracefulShutdown();
    }    @Bean
    public ServletWebServerFactory servletContainer() {
        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
        tomcat.addConnectorCustomizers(gracefulShutdown());        return tomcat;
    }    /**
     * 优雅关闭 Spring Boot
     */
    private class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {        private final Logger log = LoggerFactory.getLogger(GracefulShutdown.class);        private volatile Connector connector;        private final int waitTime = 30;        @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 thread pool did not shut down gracefully within " + waitTime + " seconds. Proceeding with forceful shutdown");
                    }
                } catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }

}

Undertow 优雅停机

@SpringBootApplicationpublic 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
            }
        }
    }
}
@Componentpublic 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@AllArgsConstructorpublic 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;
    }

}

Jetty 优雅停机

默认支持所有请求完毕后再关闭,缺点:客户端接收不到响应,有待改进!



作者:Anoyi
链接:https://www.jianshu.com/p/14c0b6c389f0


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消