如何優雅關閉 Spring Boot 應用

来源:https://www.cnblogs.com/one12138/archive/2019/07/24/11241274.html
-Advertisement-
Play Games

如何優雅關閉 Spring Boot 應用 如何優雅關閉 Spring Boot 應用前言定製 Tomcat Connector 行為內嵌 Tomcat 添加 Connector 回調開啟 Shutdown Endpoint模擬測試實現自動化總結參考 如何優雅關閉 Spring Boot 應用前言定 ...


如何優雅關閉 Spring Boot 應用

如何優雅關閉 Spring Boot 應用前言定製 Tomcat Connector 行為內嵌 Tomcat 添加 Connector 回調開啟 Shutdown Endpoint模擬測試實現自動化總結參考

前言

隨著線上應用逐步採用 SpringBoot 構建,SpringBoot應用實例越來多,當線上某個應用需要升級部署時,常常簡單粗暴地使用 kill 命令,這種停止應用的方式會讓應用將所有處理中的請求丟棄,響應失敗。這樣的響應失敗尤其是在處理重要業務邏輯時需要極力避免的,那麼有什麼更好的方式來平滑地關閉 SpringBoot 應用呢?那就通過本文一起來探究吧。(本文主要針對基於Spring Boot 內嵌 Tomcat 容器作為 Web 服務的應用)

本文示例代碼可以通過下麵倉庫地址獲取:

環境支持:

  • JDK 8

  • SpringBoot 2.1.4

  • Maven 3.6.0

 

定製 Tomcat Connector 行為

要平滑關閉 Spring Boot 應用的前提就是首先要關閉其內置的 Web 容器,不再處理外部新進入的請求。為了能讓應用接受關閉事件通知的時候,保證當前 Tomcat 處理所有已經進入的請求,我們需要實現 TomcatConnectorCustomizer 介面,這個介面的源碼十分簡單,從註釋可以看出這是實現自定義 Tomcat Connector 行為的回調介面:

 

這裡如果小伙伴對 Connector 不太熟悉,我就簡單描述下: Connector 屬於 Tomcat 抽象組件,功能就是用來接受外部請求,以及內部傳遞,並返迴響應內容,是Tomcat 中請求處理和響應的重要組件,具體實現有 HTTP Connector 和 AJP Connector。

通過定製 Connector 的行為,我們就可以允許在請求處理完畢後進行 Tomcat 線程池的關閉,具體實現代碼如下:

上述代碼定義的 TIMEOUT 變數為 Tomcat 線程池延時關閉的最大等待時間,一旦超過這個時間就會強制關閉線程池,也就無法處理所有請求了,我們通過控制 Tomcat 線程池的關閉時機,來實現優雅關閉 Web 應用的功能。另外需要註意的是我們的類 CustomShutdown 實現了 ApplicationListener<ContextClosedEvent> 介面,意味著監聽著 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 容器也類似方式,希望這邊文章有所幫助,若有錯誤或者不當之處,還請大家批評指正,一起學習交流。

參考


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 其實在JAVA開發中servlet配置,映射註入配置等等都可以用xml來配置 在此處的department是實體類的名字,而不是對應的資料庫表的名字 資料庫表的欄位名=#{實體類屬性名} 逆向工程生成的XML文件有查找更新等功能,但是當我們查找的時候需要返回一個類, 我們應該在開頭寫返回結果 res ...
  • 在Java開發中,我們最常見到最頻繁使用的就是HashMap和HashTable,但是線上程競爭激烈的併發場景中使用都不夠合理。 1、HashMap 眾所周知 HashMap 底層是基於數組 + 鏈表組成的,不過在 jdk1.7 與1.8 中具體實現稍有不同。 HashMap是線程不安全的,在併發( ...
  • capture的作用是: 捕獲模板輸出的數據並將其存儲到一個變數,而不是把它們輸出到頁面,任何在 {capture name="foo"}和{/capture}之間的數據將被存儲到變數$foo中,該變數由name屬性指定,在模板中通過 $smarty.capture.foo 訪問該變數,{captu ...
  • 21.閉包 1. 閉包:在嵌套函數內,使用非全局變數(且不使用本層變數) 2. 閉包的作用:1.保證數據的安全性(純潔度)。2.裝飾器使用 3. ._\_closure\_\_判斷是否是閉包 22.裝飾器一(入門) 1.一個裝飾器裝飾多個函數 開放封閉原則:擴展是開放的(增加新功能),源碼是封閉的( ...
  • 在以往的對象模型編碼時,我們需要寫一大堆的get/set以及不同的構造函數等。Lombok為我們提供了一個非常好的插件形式。 在大多數的項目中,只需要使用到以下集中Annotation就足夠了,如果需要查看更多的選項,請參考: "傳送門" 1. 2. 3. 4. 生成final 欄位的構造函數 5. ...
  • Fermat's theorem states that for any prime number p and for any integer a > 1, ap = a (mod p). That is, if we raise a to the pth power and divide by p ...
  • shiro是一個強大而且易用的安全框架(主要包括認證和授權),它比spring security更加簡單,而且它不依賴於任何容器,可以和許多框架集成。 shiro的核心是安全管理器(SecurityManagement),它主要包括四個模塊: 1.Authentication:認證模塊,主要用於驗證 ...
  • 前言 今天上午被 Flink 的一個運算元困惑了下,具體問題是什麼呢? 我有這麼個需求:有不同種類型的告警數據流(包含恢複數據),然後我要將這些數據流做一個拆分,拆分後的話,每種告警裡面的數據又想將告警數據和恢複數據拆分出來。 結果,這個需求用 Flink 的 Split 運算符出現了問題。 分析 需 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...