回復“資源”獲取獨家整理的學習資料! 前言隨著線上應用逐步采用 SpringBoot 構(gòu)建,SpringBoot應用實例越來多,當線上某個應用需要升級部署時,常常簡單粗暴地使用 kill 命令,這種停止應用的方式會讓應用將所有處理中的請求丟棄,響應失敗。這樣的響應失敗尤其是在處理重要業(yè)務邏輯時需要極力避免的,那么有什么更好的方式來平滑地關(guān)閉 SpringBoot 應用呢?那就通過本文一起來探究吧。(本文主要針對基于Spring Boot 內(nèi)嵌 Tomcat 容器作為 Web 服務的應用)
定制 Tomcat Connector 行為要平滑關(guān)閉 Spring Boot 應用的前提就是首先要關(guān)閉其內(nèi)置的 Web 容器,不再處理外部新進入的請求。為了能讓應用接受關(guān)閉事件通知的時候,保證當前 Tomcat 處理所有已經(jīng)進入的請求,我們需要實現(xiàn) TomcatConnectorCustomizer 接口,這個接口的源碼十分簡單,從注釋可以看出這是實現(xiàn)自定義 Tomcat Connector 行為的回調(diào)接口: 這里如果小伙伴對 Connector 不太熟悉,我就簡單描述下:Connector 屬于 Tomcat 抽象組件,功能就是用來接受外部請求,以及內(nèi)部傳遞,并返回響應內(nèi)容,是Tomcat 中請求處理和響應的重要組件,具體實現(xiàn)有 HTTP Connector 和 AJP Connector。 通過定制 Connector 的行為,我們就可以允許在請求處理完畢后進行 Tomcat 線程池的關(guān)閉,具體實現(xiàn)代碼如下: 上述代碼定義的 TIMEOUT 變量為 Tomcat 線程池延時關(guān)閉的最大等待時間,一旦超過這個時間就會強制關(guān)閉線程池,也就無法處理所有請求了,我們通過控制 Tomcat 線程池的關(guān)閉時機,來實現(xiàn)優(yōu)雅關(guān)閉 Web 應用的功能。另外需要注意的是我們的類 CustomShutdown 實現(xiàn)了 ApplicationListener 內(nèi)嵌 Tomcat 添加 Connector 回調(diào)有了定制的 Connector 回調(diào),我們需要在啟動過程中添加到內(nèi)嵌的 Tomcat 容器中,然后等待執(zhí)行。那這一步又是如何實現(xiàn)的呢,可以參考下面代碼: 這里的 TomcatServletWebServerFactory 是 Spring Boot 實現(xiàn)內(nèi)嵌 Tomcat 的工廠類,類似的其他 Web 容器,也有對應的工廠類如 JettyServletWebServerFactory,UndertowServletWebServerFactory。他們共同的特點就是繼承同個抽象類 AbstractServletWebServerFactory,提供了 Web 容器默認的公共實現(xiàn),如應用上下文設(shè)置,會話管理等。 如果我們需要定義Spring Boot 內(nèi)嵌的 Tomcat 容器時,就可以使用 TomcatServletWebServerFactory 來進行個性化定義,例如下方為官方文檔提供自定示例: 好了說回正題,我們這里使用 開啟 Shutdown Endpoint到目前讓內(nèi)嵌 Tomcat 容器平穩(wěn)關(guān)閉的操作已經(jīng)完成,接下來要做的就是如何關(guān)閉主動關(guān)閉 Spring 容器了,除了常規(guī)Linux 命令 Kill,我們可以利用 Spring Boot Actuator 來實現(xiàn)Spring 容器的遠程關(guān)閉,怎么實現(xiàn)繼續(xù)看 Spring Boot Actuator 是 Spring Boot 的一大特性,它提供了豐富的功能來幫助我們監(jiān)控和管理生產(chǎn)環(huán)境中運行的 Spring Boot 應用。我們可以通過 HTTP 或者 JMX 方式來對我們應用進行管理,除此之外,它為我們的應用提供了審計,健康狀態(tài)和度量信息收集的功能,能幫助我們更全面地了解運行中的應用。
在 Spring Boot Actuator 中也提供控制應用關(guān)閉的功能,所以我們要為應用引入 Spring Boot Actuator,具體方式就是要將對應的 starter 依賴添加到當前項目中,以 Maven 項目為例: Spring Boot Actuator 采用向外部暴露 Endpoint (端點)的方式來讓我們與應用進行監(jiān)控和管理,引入 第一行表示啟用 Shutdown Endpoint ,第二行表示向外部以 HTTP 方式暴露所有 Endpoint,默認情況下除了 Shutdown Endpoint 之外,其他 Endpoint 都是啟用的。
到這里我們的前期配置工作就算完成了。當啟動應用后,就可以通過POST 方式請求對應路徑的 模擬測試這里為了模擬測試,我們首先模擬實現(xiàn)長達10s 時間處理業(yè)務的請求控制器 BusinessController,具體實現(xiàn)如下: 用 首先用 curl 命令模擬發(fā)送業(yè)務請求: 然后在業(yè)務處理中,直接發(fā)送請求
![]() 最后看下控制臺的日志輸出順序: ![]() 可以看出在發(fā)送業(yè)務請求之后立刻發(fā)送關(guān)閉應用的請求,并不會立即將應用停止,而是在請求處理完畢之后,就是阻塞的 10s 后應用開始退出,這樣可以保證已經(jīng)接收到的請求能返回正常響應, 而關(guān)閉請求之后再進入的請求都不會被處理,到這里我們優(yōu)雅關(guān)閉 Spring Boot 程序的操作就此實現(xiàn)了。 實現(xiàn)自動化由于 Spring Boot 提供內(nèi)嵌 Web 容器的便利性,我們經(jīng)常將程序打包成 jar 然后發(fā)布。通常應用的啟動和關(guān)閉操作流程是固定且重復的,本著 Don't Repeat Yourself 原則,我們有必要將這個操作過程自動化,將關(guān)閉和啟用的 SpringBoot應用的操作寫成 shell 腳本,以避免出現(xiàn)人為的差錯,并且方便使用,提高操作效率。下面是我針對示例程序所寫的程序啟動腳本:(具體腳本可在示例項目查看) ![]() 有了腳本,我們可以直接通過命令行方式平滑地更新部署 Spring Boot 程序,效果如下: ![]() 總結(jié)本文主要探究了如何對基于Spring Boot 內(nèi)嵌 Tomcat 的 Web 應用進行平滑關(guān)閉的實現(xiàn),如果采用其他 Web 容器也類似方式,希望這邊文章有所幫助,若有錯誤或者不當之處,還請大家批評指正,一起學習交流。 |
|