SpringBoot如何優雅關閉(SpringBoot2.3&Spring Boot2.2)
優雅停止&暴力停止
- 暴力停止:像日常開發過程中,測試區或者本地開發時,我們並不會考慮專案關閉帶來的影響,只是想最快速的關掉重啟,所以用的最多的就是kill -9進行暴力停止服務;kill -9的結果就是強制關閉,不會等待服務釋放資源等操作,這也造成了,服務中很多程式無法正常結束。
- 優雅停止:何謂優雅停止,就是等待已有的程式結束之後關閉服務,那麼如何實現優雅停止SpringBoot服務?
實現優雅停止
SpringBoot要實現優雅停止,分兩種情況一個是SpringBoot版本為2.3.0之前,一種是2.3.0及往後的版本。
-
SpringBoot 2.3.0及後續版本
在SpringBoot的ReleaseNotes中我們可以看到,在2.3.0版本,SpringBoot新特性中有一個叫GraceFul shutdown的字樣。
意思就是,可以通過配置server.shutdown來實現優雅關閉SpriingBoot服務,支援內嵌的Jetty,Reactor,Netty,Undertow伺服器,在配置了優雅停止的情況下關閉服務,服務將不會接收後續請求, 並且會等待寬限期,以便完成當前已有請求。
配置解釋:
- server.shutdown : graceful : 表示開啟優雅停止
- spring.lifecycle.timeout-per-shutdown-phase : 60 :表示最大等待的寬限時間,超過這個時間還有請求沒結束的話,會強制關閉服務。
注意:這裡需要注意的是,不能使用kill -9的命令停止服務,不然優雅停止的配置不會生效。
-
SpringBoot2.3.0之前的版本
在上述的ReleaseNotes看到,配置的方式實現優雅停止時2.3.0之後才有的功能,那往前的版本怎麼辦?自己手動寫。
@Slf4j @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) public class NginxTestApplication { public static void main(String[] args) { SpringApplication.run(NginxTestApplication.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 static class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> { private volatile Connector connector; //獲取tomcat的connector @Override public void customize(Connector connector) { this.connector = connector; } //監聽事件 @Override public void onApplicationEvent(ContextClosedEvent contextClosedEvent) { //拒絕停機操作的後續請求 this.connector.pause(); log.info("停止Tomcat connector, 拒絕接收後續請求"); //獲取對應執行緒池並做對應型別判斷,true則開始優雅關閉 Executor executor = this.connector.getProtocolHandler().getExecutor(); if (executor instanceof ThreadPoolExecutor) { try { ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor; //開始關閉執行緒池 threadPoolExecutor.shutdown(); log.info("開始關閉執行緒池"); //最大寬限時間 int waitTime = 30; //若執行緒池中有未完成事件,等待完成後關閉,若超過最大寬限時間未完成,強制關閉; //若沒有未完成事件,直接關閉 if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) { log.info("Tomcat執行緒池無法在"+waitTime+"s 內優雅關閉. 強制結束"); } } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } } } } }
在啟動類中加上這些東西就行了,寫了很多註釋,就不詳細解釋了。
注意:這裡同樣需要注意的是,不能使用kill -9的命令停止服務,不然優雅停止不會生效。