天天看點

springboot 優雅停機

  最近上司要求我處理公司目前的一個痛點,他們每次發版都是直接将一個程序kill -9 結束程序,然後将新包替換上去,這樣會導緻一兩秒内某些請求服務不可用,而且正在工作的線程會被立即中斷,我心裡想,那麼暴力,還好是家小公司,業務量不會那麼大,不然很多使用者會奔潰的。

  我一開始跟上司提出使用灰階方案A/B切換來做,就是準備兩台環境,然後兩套環境進行來回切換,進而實作無縫替換新包,可惜上司不太願意接受這套方案,很尴尬!

  但是,上司跟我講

springboot

有提供優雅停機配置,你去研究一下。然後我就去查了相關的資料,确實有這麼一個東西。突然,我想到

Dubbo

也有提供優雅停機的配置,于是,我就去看了官網的配置,

Dubbo

優雅停機有個缺陷:

Dubbo

無法做到等待

Dubbo

中的使用者線程以外的線程處理完才停機,需要自己對Dubbo的優雅停機功能進行擴充。

  現在我們是基于springboot來實作優雅停機,但是springboot 1.x和2.x配置還是有點差別的,下面來看下springboot 1.x的配置:

springboot 1.x 配置
/**
     * 用于接受shutdown事件
     *
     * @return
     */
    @Bean
    public MyShutdown myShutdown() {
        return new MyShutdown();
    }
    /**
     * 用于注入 connector
     *
     * @return
     */
    @Bean
    public EmbeddedServletContainerCustomizer tomcatCustomizer() {
        return new EmbeddedServletContainerCustomizer() {
            @Override
            public void customize(ConfigurableEmbeddedServletContainer container) {
                if (container instanceof TomcatEmbeddedServletContainerFactory) {
                    ((TomcatEmbeddedServletContainerFactory) container).addConnectorCustomizers(myShutdown());
                }
            }
        };
    }
    private static class MyShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
        private static final Logger log = LoggerFactory.getLogger(MyShutdown.class);
        private volatile Connector connector;
        private final int waitTime = 100;
        @Override
        public void customize(Connector connector) {
            this.connector = connector;
        }

        @Override
        public void onApplicationEvent(ContextClosedEvent event) {
            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();
                }
            }
        }
    }
           
springboot 2.x 配置
/**
     * 用于接受shutdown事件
     *
     * @return
     */
    @Bean
    public MyShutdown myShutdown() {
        return new MyShutdown();
    }

    /**
     * 用于注入 connector
     *
     * @return
     */
    @Bean
    public WebServerFactoryCustomizer tomcatCustomizer() {
        return (WebServerFactoryCustomizer<ConfigurableWebServerFactory>) container -> {
            if (container instanceof TomcatServletWebServerFactory) {
                ((TomcatServletWebServerFactory) container).addConnectorCustomizers(myShutdown());
            }
        };
    }

    private static class MyShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
        private static final Logger log = LoggerFactory.getLogger(MyShutdown.class);
        private volatile Connector connector;
        private final int waitTime = 100;
        @Override
        public void customize(Connector connector) {
            this.connector = connector;
        }
        @Override
        public void onApplicationEvent(ContextClosedEvent event) {
            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();
                }
            }
        }
    }