一文帶你了解SpringBoot的停機(jī)方式
1. 介紹
簡(jiǎn)單的說(shuō),就是向應(yīng)用進(jìn)程發(fā)出停止指令之后,能保證正在執(zhí)行的業(yè)務(wù)操作不受影響,直到操作運(yùn)行完畢之后再停止服務(wù)。應(yīng)用程序接收到停止指令之后,會(huì)進(jìn)行如下操作:
1.停止接收新的訪問(wèn)請(qǐng)求
2.正在處理的請(qǐng)求,等待請(qǐng)求處理完畢;對(duì)于內(nèi)部正在執(zhí)行的其他任務(wù),比如定時(shí)任務(wù)、mq 消費(fèi)等等,也要等當(dāng)前正在執(zhí)行的任務(wù)執(zhí)行完畢,并且不再啟動(dòng)新的任務(wù)
3.當(dāng)應(yīng)用準(zhǔn)備關(guān)閉的時(shí)候,按需向外發(fā)出信號(hào),告知其他應(yīng)用服務(wù)準(zhǔn)備接手,以保證服務(wù)高可用
如果暴力的關(guān)閉應(yīng)用程序,比如通過(guò)kill -9 命令強(qiáng)制直接關(guān)閉應(yīng)用程序進(jìn)程,可能會(huì)導(dǎo)致正在執(zhí)行的任務(wù)數(shù)據(jù)丟失或者錯(cuò)亂,也可能會(huì)導(dǎo)致任務(wù)所持有的全局資源等不到釋放,比如當(dāng)前任務(wù)持有 redis 的鎖,并且沒(méi)有設(shè)置過(guò)期時(shí)間,當(dāng)任務(wù)突然被終止并且沒(méi)有主動(dòng)釋放鎖,會(huì)導(dǎo)致其他進(jìn)程因無(wú)法獲取鎖而不能處理業(yè)務(wù)。
那么如何在不影響正在執(zhí)行的業(yè)務(wù)的情況下,將應(yīng)用程序安全的進(jìn)行關(guān)閉呢?
2. 停機(jī)方式
2.1 使用ApplicationContext的close方法關(guān)閉服務(wù)
可以使用ApplicationContext的close方法來(lái)關(guān)停服務(wù),他會(huì)自動(dòng)銷毀bean對(duì)象并關(guān)停服務(wù)
@SpringBootApplication public class Application { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(Application.class, args); try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } //啟動(dòng)10秒以后,自動(dòng)關(guān)閉 context.close(); } }
2.2 使用SpringApplication的exit方法關(guān)閉服務(wù)
通過(guò)調(diào)用一個(gè)SpringApplication.exit()方法也可以安全的退出程序,同時(shí)會(huì)返回一個(gè)退出碼,這個(gè)退出碼可以傳遞給所有的context,最后通過(guò)調(diào)用System.exit()可以將這個(gè)錯(cuò)誤碼也傳給JVM。
@SpringBootApplication public class Application { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(Application.class, args); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } //5秒后,關(guān)閉服務(wù) exitApplication(context); } public static void exitApplication(ConfigurableApplicationContext context) { //獲取退出碼 int exitCode = SpringApplication.exit(context, (ExitCodeGenerator) () -> 0); //退出碼傳遞給jvm,安全退出程序 System.exit(exitCode); } }
3. 停機(jī)處理
3.1 DisposableBean
SpringBoot 官方文檔上,已經(jīng)告訴開發(fā)者只需要實(shí)現(xiàn)特定接口即可監(jiān)聽到項(xiàng)目啟動(dòng)成功與關(guān)閉時(shí)的事件,相關(guān)接口如下:
CommandLineRunner接口:當(dāng)應(yīng)用啟動(dòng)成功后但在開始接受流量之前,會(huì)回調(diào)此接口的實(shí)現(xiàn)類,也可以實(shí)現(xiàn)ApplicationRunner接口,工作的方式與CommandLineRunner與之類似
DisposableBean接口:當(dāng)應(yīng)用正要被銷毀前,會(huì)回調(diào)此接口的實(shí)現(xiàn)類,也可以使用@PreDestroy注解,被標(biāo)記的方法也會(huì)被調(diào)用
基于此流程,我們可以創(chuàng)建一個(gè)服務(wù)監(jiān)聽類,用于監(jiān)聽到項(xiàng)目啟動(dòng)成功與關(guān)閉時(shí)的回調(diào)服務(wù),示例代碼如下:
@Component public class AppListener implements CommandLineRunner, DisposableBean { @Override public void run(String... args) throws Exception { System.out.println("應(yīng)用啟動(dòng)成功,預(yù)加載相關(guān)數(shù)據(jù)"); } @Override public void destroy() throws Exception { System.out.println("應(yīng)用正在關(guān)閉,清理相關(guān)數(shù)據(jù)"); } }
3.2 PreDestroy
上文中,我們提到了實(shí)現(xiàn)DisposableBean接口,可以監(jiān)聽?wèi)?yīng)用關(guān)閉前的回調(diào)處理,其實(shí)在自定義的方法上加@PreDestroy注解,也可以實(shí)現(xiàn)相同的效果。
@Component public class AppDestroyConfig { @PreDestroy public void PreDestroy(){ System.out.println("應(yīng)用程序正在關(guān)閉。。。"); } }
3.3 ApplicationListener
如果有些服務(wù),比如定時(shí)任務(wù),我們想在SpringBoot關(guān)閉數(shù)據(jù)源連接池之前,將其關(guān)閉,可以通過(guò)實(shí)現(xiàn)ApplicationListener接口,監(jiān)聽bean對(duì)象的變化情況,在bean對(duì)象銷毀之前,執(zhí)行相關(guān)的關(guān)閉任務(wù)。
@Component public class JobTaskListener implements ApplicationListener { @Override public void onApplicationEvent(ApplicationEvent applicationEvent) { // 在spring bean容器銷毀之前執(zhí)行的事件,防止數(shù)據(jù)庫(kù)連接池在任務(wù)終止前銷毀 if (applicationEvent instanceof ContextClosedEvent) { System.out.println("關(guān)閉相關(guān)的定時(shí)任務(wù)"); } } }
到此這篇關(guān)于一文帶你了解SpringBoot的停機(jī)方式的文章就介紹到這了,更多相關(guān)SpringBoot停機(jī)方式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot訪問(wèn)404問(wèn)題的解決辦法
工作中遇到url404問(wèn)題,解決問(wèn)題的進(jìn)程比較崎嶇,寫篇文章記錄,下面這篇文章主要給大家介紹了關(guān)于springboot訪問(wèn)404問(wèn)題的解決辦法,文中通過(guò)圖文介紹的非常詳細(xì),要的朋友可以參考下2023-03-03Spring?Boot集成RabbitMQ以及隊(duì)列模式操作
RabbitMQ是實(shí)現(xiàn)AMQP(高級(jí)消息隊(duì)列協(xié)議)的消息中間件的一種,下面這篇文章主要給大家介紹了關(guān)于Spring?Boot集成RabbitMQ以及隊(duì)列模式操作的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-04-04解決springboot jpa @Column columnDefinition等屬性失效問(wèn)題
這篇文章主要介紹了解決springboot jpa @Column columnDefinition等屬性失效問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10詳解Java構(gòu)建樹結(jié)構(gòu)的公共方法
本文主要介紹了詳解Java構(gòu)建樹結(jié)構(gòu)的公共方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04解決因jdk版本引起的TypeNotPresentExceptionProxy異常
這篇文章介紹了解決因jdk版本引起的TypeNotPresentExceptionProxy異常的方法,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-12-12詳解IDEA多module項(xiàng)目maven依賴的一些說(shuō)明
這篇文章主要介紹了詳解IDEA多module項(xiàng)目maven依賴的一些說(shuō)明,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-10-10java 定義長(zhǎng)度為0的數(shù)組/空數(shù)組案例
這篇文章主要介紹了java 定義長(zhǎng)度為0的數(shù)組/空數(shù)組案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-03-03Spring MVC 中攔截器的使用示例詳解"攔截器基本配置"和 &q
Spring MVC 的攔截器作用是在請(qǐng)求到達(dá)控制器之前或之后進(jìn)行攔截,可以對(duì)請(qǐng)求和響應(yīng)進(jìn)行一些特定的處理,這篇文章主要介紹了Spring MVC 中的攔截器的使用“攔截器基本配置” 和 “攔截器高級(jí)配置”,需要的朋友可以參考下2024-07-07