1. 程式人生 > >Spring Boot 系列:最新版優雅停機詳解

Spring Boot 系列:最新版優雅停機詳解

> 愛生活,愛編碼,本文已收錄[架構技術專欄](http://www.jiagoujishu.com/)關注這個喜歡分享的地方。 開源專案: - 分散式監控(Gitee GVP最有價值開源專案 ):https://gitee.com/sanjiankethree/cubic - 攝像頭視訊流採集:https://gitee.com/sanjiankethree/cubic-video ## 優雅停機 目前Spring Boot已經發展到了2.3.4.RELEASE,伴隨著2.3版本的到來,優雅停機機制也更加完善了。 目前版本的Spring Boot 優雅停機支援Jetty, Reactor Netty, Tomcat和 Undertow 以及反應式和基於 Servlet 的 web 應用程式都支援優雅停機功能。 **優雅停機的目的:** 如果沒有優雅停機,伺服器此時直接直接關閉(kill -9),那麼就會導致當前正在容器內執行的業務直接失敗,在某些特殊的場景下產生髒資料。 **增加了優雅停機配置後:** 在伺服器執行關閉(kill -2)時,會預留一點時間使容器內部業務執行緒執行完畢,此時容器也不允許新的請求進入。新請求的處理方式跟web伺服器有關,Reactor Netty、 Tomcat將停止接入請求,Undertow的處理方式是返回503. ## 新版配置 #### YAML配置 新版本配置非常簡單,server.shutdown=graceful 就搞定了(注意,優雅停機配置需要配合Tomcat 9.0.33(含)以上版本) ```yaml server: port: 6080 shutdown: graceful #開啟優雅停機 spring: lifecycle: timeout-per-shutdown-phase: 20s #設定緩衝時間 預設30s ``` 在設定了緩衝引數timeout-per-shutdown-phase 後,在規定時間內如果執行緒無法執行完畢則會被強制停機。 下面我們來看下停機時,加了優雅停日誌和不加的區別: **** ```java //未加優雅停機配置 Disconnected from the target VM, address: '127.0.0.1:49754', transport: 'socket' Process finished with exit code 130 (interrupted by signal 2: SIGINT) ``` 加了優雅停機配置後,可明顯發現的日誌 **Waiting for active requests to cpmplete**,此時容器將在ShutdownHook執行完畢後停止。 ![](https://imgkr2.cn-bj.ufileos.com/6bba6a13-beb4-466e-942d-76828d74e99c.jpg?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=VjHXDoKrahydmHRzhuUw05JFkjU%253D&Expires=1602773654) #### 關閉方式 1、 一定不要使用kill -9 操作,使用kill -2 來關閉容器。這樣才會觸發java內部ShutdownHook操作,kill -9不會觸發ShutdownHook。 2、可以使用端點監控 POST 請求 **/actuator/shutdown** 來執行優雅關機。 #### 新增ShutdownHook 通過上面的日誌我們發現Druid執行了自己的ShutdownHook,那麼我們也來新增下ShutdownHook,有幾種簡單的方式: 1、實現DisposableBean介面,實現destroy方法 ```java @Slf4j @Service public class DefaultDataStore implements DisposableBean { private final ExecutorService executorService = new ThreadPoolExecutor(OSUtil.getAvailableProcessors(), OSUtil.getAvailableProcessors() + 1, 1, TimeUnit.MINUTES, new ArrayBlockingQueue<>(200), new DefaultThreadFactory("UploadVideo")); @Override public void destroy() throws Exception { log.info("準備優雅停止應用使用 DisposableBean"); executorService.shutdown(); } } ``` 2、使用@PreDestroy註解 ```java @Slf4j @Service public class DefaultDataStore { private final ExecutorService executorService = new ThreadPoolExecutor(OSUtil.getAvailableProcessors(), OSUtil.getAvailableProcessors() + 1, 1, TimeUnit.MINUTES, new ArrayBlockingQueue<>(200), new DefaultThreadFactory("UploadVideo")); @PreDestroy public void shutdown() { log.info("準備優雅停止應用 @PreDestroy"); executorService.shutdown(); } } ``` 這裡注意,@PreDestroy 比 DisposableBean 先執行 #### 關閉原理 1、使用kill pid關閉,原始碼很簡單,大家可以看下GracefulShutdown ```java private void doShutdown(GracefulShutdownCallback callback)