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

Anoyi 精讲JAVA 精讲JAVA Spring Boot 内嵌容器 Tomcat / Undertow / Jetty 优雅停机实现 随笔 第1张

微信号 toooooooozi

功能介绍 讲解java深层次开发,解析各大流行框架的源码

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。
2018-04-19

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 优雅停机

   
 
  1. @SpringBootApplication

  2. public class Application {

  3.    public static void main(String[] args) {

  4.        SpringApplication.run(Application.class, args);

  5.    }

  6.    /**

  7.     * 用于接受 shutdown 事件

  8.     */

  9.    @Bean

  10.    public GracefulShutdown gracefulShutdown() {

  11.        return new GracefulShutdown();

  12.    }

  13.    @Bean

  14.    public ServletWebServerFactory servletContainer() {

  15.        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();

  16.        tomcat.addConnectorCustomizers(gracefulShutdown());

  17.        return tomcat;

  18.    }

  19.    /**

  20.     * 优雅关闭 Spring Boot

  21.     */

  22.    private class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {

  23.        private final Logger log = LoggerFactory.getLogger(GracefulShutdown.class);

  24.        private volatile Connector connector;

  25.        private final int waitTime = 30;

  26.        @Override

  27.        public void customize(Connector connector) {

  28.            this.connector = connector;

  29.        }

  30.        @Override

  31.        public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {

  32.            this.connector.pause();

  33.            Executor executor = this.connector.getProtocolHandler().getExecutor();

  34.            if (executor instanceof ThreadPoolExecutor) {

  35.                try {

  36.                    ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;

  37.                    threadPoolExecutor.shutdown();

  38.                    if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) {

  39.                        log.warn("Tomcat thread pool did not shut down gracefully within " + waitTime + " seconds. Proceeding with forceful shutdown");

  40.                    }

  41.                } catch (InterruptedException ex) {

  42.                    Thread.currentThread().interrupt();

  43.                }

  44.            }

  45.        }

  46.    }

  47. }

Undertow 优雅停机

   
 
  1. @SpringBootApplication

  2. public class Application {

  3.    public static void main(String[] args) {

  4.        SpringApplication.run(Application.class, args);

  5.    }

  6.    /**

  7.     * 优雅关闭 Spring Boot

  8.     */

  9.    @Component

  10.    public class GracefulShutdown implements ApplicationListener<ContextClosedEvent> {

  11.        @Autowired

  12.        private GracefulShutdownWrapper gracefulShutdownWrapper;

  13.        @Autowired

  14.        private ServletWebServerApplicationContext context;

  15.        @Override

  16.        public void onApplicationEvent(ContextClosedEvent contextClosedEvent){

  17.            gracefulShutdownWrapper.getGracefulShutdownHandler().shutdown();

  18.            try {

  19.                UndertowServletWebServer webServer = (UndertowServletWebServer)context.getWebServer();

  20.                Field field = webServer.getClass().getDeclaredField("undertow");

  21.                field.setAccessible(true);

  22.                Undertow undertow = (Undertow) field.get(webServer);

  23.                List<Undertow.ListenerInfo> listenerInfo = undertow.getListenerInfo();

  24.                Undertow.ListenerInfo listener = listenerInfo.get(0);

  25.                ConnectorStatistics connectorStatistics = listener.getConnectorStatistics();

  26.                while (connectorStatistics.getActiveConnections() > 0){}

  27.            }catch (Exception e){

  28.                // Application Shutdown

  29.            }

  30.        }

  31.    }

  32. }

   
 
  1. @Component

  2. public class GracefulShutdownWrapper implements HandlerWrapper{

  3.    private GracefulShutdownHandler gracefulShutdownHandler;

  4.    @Override

  5.    public HttpHandler wrap(HttpHandler handler) {

  6.        if(gracefulShutdownHandler == null) {

  7.            this.gracefulShutdownHandler = new GracefulShutdownHandler(handler);

  8.        }

  9.        return gracefulShutdownHandler;

  10.    }

  11.    public GracefulShutdownHandler getGracefulShutdownHandler() {

  12.        return gracefulShutdownHandler;

  13.    }

  14. }

   
 
  1. @Component

  2. @AllArgsConstructor

  3. public class UndertowExtraConfiguration {

  4.    private final GracefulShutdownWrapper gracefulShutdownWrapper;

  5.    @Bean

  6.    public UndertowServletWebServerFactory servletWebServerFactory() {

  7.        UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();

  8.        factory.addDeploymentInfoCustomizers(deploymentInfo -> deploymentInfo.addOuterHandlerChainWrapper(gracefulShutdownWrapper));

  9.        factory.addBuilderCustomizers(builder -> builder.setServerOption(UndertowOptions.ENABLE_STATISTICS, true));

  10.        return factory;

  11.    }

  12. }

Jetty 优雅停机

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

相关内容

Shut down embedded servlet container gracefully

转载声明:本文转载自「SpringForAll社区」

Spring Boot 内嵌容器 Tomcat / Undertow / Jetty 优雅停机实现 随笔 第2张 Anoyi

赞赏

长按二维码向我转账

Spring Boot 内嵌容器 Tomcat / Undertow / Jetty 优雅停机实现 随笔 第3张

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄