摘要:除了,還有十余種,有的是特定操作,比如轉儲內存日志有的是信息展示,比如顯示應用健康狀態。
前言
隨著線上應用逐步采用 SpringBoot 構建,SpringBoot應用實例越來多,當線上某個應用需要升級部署時,常常簡單粗暴地使用 kill 命令,這種停止應用的方式會讓應用將所有處理中的請求丟棄,響應失敗。這樣的響應失敗尤其是在處理重要業務邏輯時需要極力避免的,那么有什么更好的方式來平滑地關閉 SpringBoot 應用呢?那就通過本文一起來探究吧。(本文主要針對基于Spring Boot 內嵌 Tomcat 容器作為 Web 服務的應用)
定制 Tomcat Connector 行為本文示例代碼可以通過下面倉庫地址獲取:
springboot-shutdown:https://github.com/wrcj12138a...
環境支持:
JDK 8
SpringBoot 2.1.4
Maven 3.6.0
要平滑關閉 Spring Boot 應用的前提就是首先要關閉其內置的 Web 容器,不再處理外部新進入的請求。為了能讓應用接受關閉事件通知的時候,保證當前 Tomcat 處理所有已經進入的請求,我們需要實現 TomcatConnectorCustomizer 接口,這個接口的源碼十分簡單,從注釋可以看出這是實現自定義 Tomcat Connector 行為的回調接口:
這里如果小伙伴對 Connector 不太熟悉,我就簡單描述下:Connector 屬于 Tomcat 抽象組件,功能就是用來接受外部請求,以及內部傳遞,并返回響應內容,是Tomcat 中請求處理和響應的重要組件,具體實現有 HTTP Connector 和 AJP Connector。
通過定制 Connector 的行為,我們就可以允許在請求處理完畢后進行 Tomcat 線程池的關閉,具體實現代碼如下:
上述代碼定義的 TIMEOUT 變量為 Tomcat 線程池延時關閉的最大等待時間,一旦超過這個時間就會強制關閉線程池,也就無法處理所有請求了,我們通過控制 Tomcat 線程池的關閉時機,來實現優雅關閉 Web 應用的功能。另外需要注意的是我們的類 CustomShutdown 實現了 ApplicationListener 接口,意味著監聽著 Spring 容器關閉的事件,即當前的 ApplicationContext 執行 close 方法。
內嵌 Tomcat 添加 Connector 回調有了定制的 Connector 回調,我們需要在啟動過程中添加到內嵌的 Tomcat 容器中,然后等待執行。那這一步又是如何實現的呢,可以參考下面代碼:
這里的 TomcatServletWebServerFactory 是 Spring Boot 實現內嵌 Tomcat 的工廠類,類似的其他 Web 容器,也有對應的工廠類如 JettyServletWebServerFactory,UndertowServletWebServerFactory。他們共同的特點就是繼承同個抽象類 AbstractServletWebServerFactory,提供了 Web 容器默認的公共實現,如應用上下文設置,會話管理等。
如果我們需要定義Spring Boot 內嵌的 Tomcat 容器時,就可以使用 TomcatServletWebServerFactory 來進行個性化定義,例如下方為官方文檔提供自定示例:
好了說回正題,我們這里使用 addConnectorCustomizers 方法將自定義的 Connector 行為添加到內嵌的Tomcat 之上,為了查看加載效果,我們可以在 Spring Boot 程序啟動后從容器中獲取下webServerFactory 對象,然后觀察,在它的 tomcatConnectorCustomizers 屬性中可以看到已經有了 CustomeShutdown 對象。
開啟 Shutdown Endpoint到目前讓內嵌 Tomcat 容器平穩關閉的操作已經完成,接下來要做的就是如何關閉主動關閉 Spring 容器了,除了常規Linux 命令 Kill,我們可以利用 Spring Boot Actuator 來實現Spring 容器的遠程關閉,怎么實現繼續看
Spring Boot Actuator 是 Spring Boot 的一大特性,它提供了豐富的功能來幫助我們監控和管理生產環境中運行的 Spring Boot 應用。我們可以通過 HTTP 或者 JMX 方式來對我們應用進行管理,除此之外,它為我們的應用提供了審計,健康狀態和度量信息收集的功能,能幫助我們更全面地了解運行中的應用。
Actuator, ["?kt???e?t?] 中文翻譯過來就是制動器,這是一個制造業的術語,指的是用于控制某物的機械裝置。
在 Spring Boot Actuator 中也提供控制應用關閉的功能,所以我們要為應用引入 Spring Boot Actuator,具體方式就是要將對應的 starter 依賴添加到當前項目中,以 Maven 項目為例:
Spring Boot Actuator 采用向外部暴露 Endpoint (端點)的方式來讓我們與應用進行監控和管理,引入 spring-boot-starter-actuator 之后,我們就需要啟用我們需要的 Shutdown Endpoint,在配置文件 application.properties 中,設置如下
第一行表示啟用 Shutdown Endpoint ,第二行表示向外部以 HTTP 方式暴露所有 Endpoint,默認情況下除了 Shutdown Endpoint 之外,其他 Endpoint 都是啟用的。
除了 Shutdown Endpoint,Actuator Endpoint 還有十余種,有的是特定操作,比如 heapdump 轉儲內存日志;有的是信息展示,比如 health 顯示應用健康狀態。具體所有 Endpoint 信息可以參見官方文檔-53. Endpoints 一節。
到這里我們的前期配置工作就算完成了。當啟動應用后,就可以通過POST 方式請求對應路徑的 http://host:port/actuator/shutdown 來實現Spring Boot 應用遠程關閉,是不是很簡單呢。
模擬測試這里為了模擬測試,我們首先模擬實現長達10s 時間處理業務的請求控制器 BusinessController,具體實現如下:
用 Thread.sleep 來阻塞當前請求線程,模擬業務處理,在此同時用 HTTP 方式訪問 Shutdown Endpoint 試圖關閉應用,可以通過觀察控制臺日志看是否應用是否會完成請求的處理后才真正進行關閉。
首先用 curl 命令模擬發送業務請求:
然后在業務處理中,直接發送請求 actuator/shutdown,嘗試關閉應用,同樣采用 curl 方式:
actuator/shutdown 請求發送后會立即返回響應結果,但應用并不會停止:
最后看下控制臺的日志輸出順序:
可以看出在發送業務請求之后立刻發送關閉應用的請求,并不會立即將應用停止,而是在請求處理完畢之后,就是阻塞的 10s 后應用開始退出,這樣可以保證已經接收到的請求能返回正常響應, 而關閉請求之后再進入的請求都不會被處理,到這里我們優雅關閉 Spring Boot 程序的操作就此實現了。
實現自動化由于 Spring Boot 提供內嵌 Web 容器的便利性,我們經常將程序打包成 jar 然后發布。通常應用的啟動和關閉操作流程是固定且重復的,本著 Don"t Repeat Yourself 原則,我們有必要將這個操作過程自動化,將關閉和啟用的 SpringBoot應用的操作寫成 shell 腳本,以避免出現人為的差錯,并且方便使用,提高操作效率。下面是我針對示例程序所寫的程序啟動腳本:(具體腳本可在示例項目查看)
有了腳本,我們可以直接通過命令行方式平滑地更新部署 Spring Boot 程序,效果如下:
總結本文主要探究了如何對基于Spring Boot 內嵌 Tomcat 的 Web 應用進行平滑關閉的實現,如果采用其他 Web 容器也類似方式,希望這邊文章有所幫助,若有錯誤或者不當之處,還請大家批評指正,一起學習交流。
參考Graceful Shutdown Spring Boot Applications:https://blog.marcosbarbero.co...
Shutdown a Spring Boot Application:https://www.baeldung.com/spri...
官方文檔-53. Endpoints:https://docs.spring.io/spring...
The HTTP Connector:https://tomcat.apache.org/tom...
Customizing ConfigurableServletWebServerFactory Directly:https://docs.spring.io/spring...
推薦閱讀:
《深入理解 Java 內存模型》讀書筆記
面試-基礎篇
Spring Boot 2.0 遷移指南
SpringBoot使用Docker快速部署項目
為什么選擇 Spring 作為 Java 框架?
SpringBoot RocketMQ 整合使用和監控
Spring Boot 面試的十個問題
上篇好文:
使用 Spring Framework 時常犯的十大錯誤
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/75912.html
摘要:需要注意的是必須要使用版本為以上才支持屬性。與格式文件不同,正對不同的,無法在一個文件設置,官方采用命名形式為格式來達成一樣的效果。采用方式添加的是屬于額外激活的,也就是說覆蓋掉外部傳入的指定的。 showImg(https://segmentfault.com/img/remote/1460000019924197?w=1050&h=500); Spring Boot Profile...
摘要:探究系統登錄驗證碼的實現后端掘金驗證碼生成類手把手教程后端博客系統第一章掘金轉眼間時間就從月份到現在的十一月份了。提供了與標準不同的工作方式我的后端書架后端掘金我的后端書架月前本書架主要針對后端開發與架構。 Spring Boot干貨系列總綱 | 掘金技術征文 - 掘金原本地址:Spring Boot干貨系列總綱博客地址:http://tengj.top/ 前言 博主16年認識Spin...
摘要:在創建之前,實際上觸發了一些事件,因此不能將偵聽器注冊為。使用的事件發布機制發送應用程序事件,該機制的一部分確保在子環境中發布給偵聽器的事件也會在任何祖先上下文中被發布給監聽器。 23. SpringApplication SpringApplication類提供了一種方便的方法來引導從main()方法開始的Spring應用程序。在許多情況下,你可以委托給靜態SpringApplica...
摘要:有了配置文件之后,啟動程序,我們首先可以看到日志輸入,由此可以看出程序讀取了的配置。首先,根據的全局查找功能,直接搜索這些詞出現的位置,進行定位,可以找到這個日志出現于方法之中。由于我們的配置文件在下,所以只要留意當為的程序執行情況即可。 前言 上文《一文掌握 Spring Boot Profiles》 是對 Spring Boot Profiles 的介紹和使用,因此本文將從源碼角度...
摘要:滿足這些約束條件和原則的應用程序或設計就是。需要注意的是,是設計風格而不是標準。同一個路徑,因為請求方式的不同,而去找尋不同的接口,完成對資源狀態的轉變。一個符合風格的就可以稱之一個的接口。 RESTful 相信在座的各位對于RESTful都是略有耳聞,那么RESTful到底是什么呢? REST(Representational State Transfer)表述性狀態轉移是一組架構約...
閱讀 2299·2023-04-25 14:22
閱讀 3740·2021-11-15 18:12
閱讀 1297·2019-08-30 15:44
閱讀 3220·2019-08-29 15:37
閱讀 647·2019-08-29 13:49
閱讀 3462·2019-08-26 12:11
閱讀 877·2019-08-23 18:28
閱讀 1586·2019-08-23 14:55