0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

還在使用kill -9 pid結(jié)束spring boot項(xiàng)目嗎?

Linux愛(ài)好者 ? 來(lái)源:CSDN技術(shù)社區(qū) ? 作者:流星007 ? 2021-04-13 16:01 ? 次閱讀

kill可將指定的信息送至程序。預(yù)設(shè)的信息為SIGTERM(15),可將指定程序終止。若仍無(wú)法終止該程序,可使用SIGKILL(9)信息嘗試強(qiáng)制刪除程序。程序或工作的編號(hào)可利用ps指令或jobs指令查看(這段話來(lái)自菜鳥(niǎo)教程)。

講的這個(gè)復(fù)雜,簡(jiǎn)單點(diǎn)來(lái)說(shuō)就是用來(lái)殺死linux中的進(jìn)程,啥?你問(wèn)我啥是進(jìn)程?請(qǐng)自行百度。

我相信很多人都用過(guò)kill -9 pid 這個(gè)命令,徹底殺死進(jìn)程的意思,一般情況我們使用它沒(méi)有上面問(wèn)題,但是在我們項(xiàng)目中使用它就有可能存在致命的問(wèn)題。

kill -9 pid 帶來(lái)的問(wèn)題

由于kill -9 屬于暴力刪除,所以會(huì)給程序帶來(lái)比較嚴(yán)重的后果,那究竟會(huì)帶來(lái)什么后果呢?

舉個(gè)栗子:轉(zhuǎn)賬功能,再給兩個(gè)賬戶進(jìn)行加錢(qián)扣錢(qián)的時(shí)候突然斷電了?這個(gè)時(shí)候會(huì)發(fā)生什么事情?對(duì)于InnoDB存儲(chǔ)引擎來(lái)說(shuō),沒(méi)有什么損失,因?yàn)樗С质聞?wù),但是對(duì)于MyISAM引擎來(lái)說(shuō)那簡(jiǎn)直就是災(zāi)難,為什么?假如給A賬戶扣了錢(qián),現(xiàn)在需要將B賬戶加錢(qián),這個(gè)時(shí)候停電了,就會(huì)造成,A的錢(qián)被扣了,但是B沒(méi)有拿到這筆錢(qián),這在生產(chǎn)環(huán)境是絕對(duì)不允許的,kill -9 相當(dāng)于突然斷電的效果。

當(dāng)然了,像轉(zhuǎn)賬這種,肯定不是使用MyISAM引擎,但是如今分布式火了起來(lái),跨服務(wù)轉(zhuǎn)賬已經(jīng)是很平常的事情,這種時(shí)候如果使用kill -9 去停止服務(wù),那就不是你的事務(wù)能保證數(shù)據(jù)的準(zhǔn)確性了,這個(gè)時(shí)候你可能會(huì)想到分布式事務(wù),這個(gè)世界上沒(méi)有絕對(duì)的安全系統(tǒng)或者架構(gòu),分布式事務(wù)也是一樣,他也會(huì)存在問(wèn)題,概率很小,如果一旦發(fā)生,損失有可能是無(wú)法彌補(bǔ)的,所以一定不能使用kill -9 去停止服務(wù),因?yàn)槟悴恢浪麜?huì)造成什么后果。

在MyISAM引擎中表現(xiàn)的更明顯,比如用戶的信息由兩張表維護(hù),管理員修改用戶信息的時(shí)候需要修改兩張表,但由于你的kill -9 暴力結(jié)束項(xiàng)目,導(dǎo)致只修改成功了一張表,這也會(huì)導(dǎo)致數(shù)據(jù)的不一致性,這是小事,因?yàn)榇蟛涣嗽傩薷囊淮?,但是金錢(qián)、合同這些重要的信息如果由于你的暴力刪除導(dǎo)致錯(cuò)亂,我覺(jué)得可能比刪庫(kù)跑路還嚴(yán)重,至少刪庫(kù)還能恢復(fù),你這個(gè)都不知道錯(cuò)在哪里。

那我們應(yīng)該怎么結(jié)束項(xiàng)目呢?

其實(shí)java給我們提供了結(jié)束項(xiàng)目的功能,比如:tomcat可以使用shutdown.bat/shutdown.sh進(jìn)行優(yōu)雅結(jié)束。

什么叫優(yōu)雅結(jié)束?

第一步:停止接收請(qǐng)求和內(nèi)部線程。

第二步:判斷是否有線程正在執(zhí)行。

第三步:等待正在執(zhí)行的線程執(zhí)行完畢。

第四步:停止容器。

以上四步才是正常的結(jié)束流程,那springboot怎么正常結(jié)束服務(wù)呢?下面我介紹幾種正常結(jié)束服務(wù)的方案,請(qǐng)拿好小本本做好筆記。

優(yōu)雅結(jié)束服務(wù)

kill -15 pid

這種方式也會(huì)比較優(yōu)雅的結(jié)束進(jìn)程(項(xiàng)目),使用他的時(shí)候需要慎重,為什么呢?我們來(lái)看個(gè)例子

我寫(xiě)了一個(gè)普通的controller方法做測(cè)試

@GetMapping(value = “/test”) public String test(){ log.info(“test --- start”); try { Thread.sleep(100000); } catch (InterruptedException e) { e.printStackTrace(); } log.info(“test --- end”); return “test”; }

代碼很簡(jiǎn)單,打?。簍est — start之后讓讓程序休眠100秒,然后再打?。簍est — end,在線程休眠中我們使用kill -15 pid來(lái)結(jié)束這個(gè)進(jìn)程,你們猜 test — end會(huì)被打印嗎?

application.yml

server: port: 9988

啟動(dòng)項(xiàng)目

sudo mvn spring-boot:run

這是maven啟動(dòng)springboot項(xiàng)目的方式

27269916-9b2f-11eb-8b86-12bb97331649.png

看到這個(gè)就代表項(xiàng)目啟動(dòng)成了

找到項(xiàng)目的進(jìn)程id

sudo ps -ef |grep shutdown

27883e5a-9b2f-11eb-8b86-12bb97331649.png

這個(gè)就是項(xiàng)目的進(jìn)程號(hào),接下來(lái)我們先測(cè)試test接口,讓線程進(jìn)入休眠狀態(tài),然后再使用kill -15 14086停止項(xiàng)目

sudo curl 127.0.0.1:9988/test

回到項(xiàng)目日志

27b44838-9b2f-11eb-8b86-12bb97331649.png

我們發(fā)現(xiàn)請(qǐng)求已經(jīng)到達(dá)服務(wù),并且線程已經(jīng)成功進(jìn)入休眠,現(xiàn)在我們kill -15 14086結(jié)束進(jìn)程

pIYBAGB1UQuActwnAASaBSQr8kc513.png

居然報(bào)錯(cuò)了,但是test — end是打印出來(lái)了,為什么會(huì)報(bào)錯(cuò)呢?這就和sleep這個(gè)方法有關(guān)了,在線程休眠期間,當(dāng)調(diào)用線程的interrupt方法的時(shí)候會(huì)導(dǎo)致sleep拋出異常,這里很明顯就是kill -15 這個(gè)命令會(huì)讓程序馬上調(diào)用線程的interrupt方法,目的是為了讓線程停止,雖然讓線程停止,但線程什么時(shí)候停止還是線程自己說(shuō)的算,這就是為什么我們還能看到:test — end的原因。

ConfigurableApplicationContext colse

我們先看怎么實(shí)現(xiàn)

package com.ymy.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; @RestController @Slf4j public class TestController implements ApplicationContextAware { private ApplicationContext context; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; } @GetMapping(value = “/test”) public String test(){ log.info(“test --- start”); try { Thread.sleep(100000); } catch (InterruptedException e) { e.printStackTrace(); } log.info(“test --- end”); return “test”; } /** * 停機(jī) */ @PostMapping(value = “shutdown”) public void shutdown(){ ConfigurableApplicationContext cyx = (ConfigurableApplicationContext) context; cyx.close(); } }

重點(diǎn)在:cyx.close();,為什么他能停止springboot項(xiàng)目呢?請(qǐng)看源碼

public void close() { synchronized(this.startupShutdownMonitor) { this.doClose(); if (this.shutdownHook != null) { try { Runtime.getRuntime().removeShutdownHook(this.shutdownHook); } catch (IllegalStateException var4) { } } } }

程序在啟動(dòng)的時(shí)候向jvm注冊(cè)了一個(gè)關(guān)閉鉤子,我們?cè)趫?zhí)行colse方法的時(shí)候會(huì)刪除這個(gè)關(guān)閉鉤子,jvm就會(huì)知道這是需要停止服務(wù)。

我們看測(cè)試結(jié)果

282fec72-9b2f-11eb-8b86-12bb97331649.png

28ea8c3a-9b2f-11eb-8b86-12bb97331649.png

很明顯,他也出發(fā)了線程的interrupt方法導(dǎo)致線程報(bào)錯(cuò),原理和kill -15差不多。

actuator

這種方式是通過(guò)引入依賴(lài)的方式停止服務(wù),actuator提供了很多接口,比如健康檢查,基本信息等等,我們也可以使用他來(lái)優(yōu)雅的停機(jī)。

引入依賴(lài)

《dependency》 《groupId》org.springframework.boot《/groupId》 《artifactId》spring-boot-starter-actuator《/artifactId》 《/dependency》

application.yml

server: port: 9988 management: endpoints: web: exposure: include: shutdown endpoint: shutdown: enabled: true server: port: 8888

我這里對(duì)actuator的接口重新給定了一個(gè)接口,這樣可提高安全性,下面我們來(lái)測(cè)試一下

@RequestMapping(value = “/test”,method = RequestMethod.GET) public String test(){ System.out.println(“test --- start”); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(“test --- end”); return “hello”; }

在請(qǐng)求test途中停止服務(wù)

28f9bb6a-9b2f-11eb-8b86-12bb97331649.png

我們發(fā)現(xiàn)發(fā)送停止服務(wù)請(qǐng)求之后還給我們返回了提示信息,很人性化,我們看看控制臺(tái)

291be906-9b2f-11eb-8b86-12bb97331649.png

292f36aa-9b2f-11eb-8b86-12bb97331649.png

test — end被執(zhí)行了,不過(guò)在停止線程池的時(shí)候還是調(diào)用了線程的interrupt方法,導(dǎo)致sleep報(bào)錯(cuò),這三種方式都可以比較優(yōu)雅的停止springboot服務(wù),如果我項(xiàng)目中存在線程休眠,我希望10秒以后再停止服務(wù)可以嗎?肯定是可以的,我們只需要稍微做點(diǎn)修改就可以了。

1.新增停止springboot服務(wù)類(lèi):ElegantShutdownConfig.java

package com.ymy.config; import org.apache.catalina.connector.Connector; import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextClosedEvent; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class ElegantShutdownConfig implements TomcatConnectorCustomizer, ApplicationListener《ContextClosedEvent》 { private volatile Connector connector; private final int waitTime = 10; @Override public void customize(Connector connector) { this.connector = connector; } @Override public void onApplicationEvent(ContextClosedEvent event) { connector.pause(); Executor executor = connector.getProtocolHandler().getExecutor(); if (executor instanceof ThreadPoolExecutor) { try { ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor; threadPoolExecutor.shutdown(); if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) { System.out.println(“請(qǐng)嘗試暴力關(guān)閉”); } } catch (InterruptedException ex) { System.out.println(“異常了”); Thread.currentThread().interrupt(); } } } }

2.在啟動(dòng)類(lèi)中加入bean

package com.ymy; import com.ymy.config.ElegantShutdownConfig; import lombok.extern.slf4j.Slf4j; import org.apache.catalina.connector.Connector; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.servlet.server.ServletWebServerFactory; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.event.ContextClosedEvent; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @SpringBootApplication public class ShutdownServerApplication { public static void main(String[] args) { ConfigurableApplicationContext run = SpringApplication.run(ShutdownServerApplication.class, args); run.registerShutdownHook(); } @Bean public ElegantShutdownConfig elegantShutdownConfig() { return new ElegantShutdownConfig(); } @Bean public ServletWebServerFactory servletContainer() { TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory(); tomcat.addConnectorCustomizers(elegantShutdownConfig()); return tomcat; } }

這樣我們就配置好了,我們?cè)賮?lái)測(cè)試一遍,test的接口還是休眠10秒

2961f3c4-9b2f-11eb-8b86-12bb97331649.png

我們發(fā)現(xiàn)這次沒(méi)有報(bào)錯(cuò)了,他是等待了一段時(shí)間之后再結(jié)束的線程池,這個(gè)時(shí)間就是我們?cè)贓legantShutdownConfig類(lèi)中配置的waitTime。

那可能你會(huì)有疑問(wèn)了,jvm沒(méi)有立即停止,那這個(gè)時(shí)候在有請(qǐng)求會(huì)發(fā)生什么呢?如果關(guān)閉的時(shí)候有新的請(qǐng)求,服務(wù)將不在接收此請(qǐng)求。

數(shù)據(jù)備份操作

如果我想在服務(wù)停止的時(shí)候做點(diǎn)備份操作啥的,應(yīng)該怎么做呢?其實(shí)很簡(jiǎn)單在你要執(zhí)行的方法上添加一個(gè)注解即可:@PreDestroy

Destroy:消滅、毀滅

pre:前綴縮寫(xiě)

所以合在一起的意思就是在容器停止之前執(zhí)行一次,你可以在這里面做備份操作,也可以做記錄停機(jī)時(shí)間等。

新增服務(wù)停止備份工具類(lèi):DataBackupConfig.java

package com.ymy.config; import org.springframework.context.annotation.Configuration; import javax.annotation.PreDestroy; @Configuration public class DataBackupConfig { @PreDestroy public void backData(){ System.out.println(“正在備份數(shù)據(jù)。。。。。。。。。。。”); } }

我們?cè)賮?lái)測(cè)試然后打印控制臺(tái)日志:

29a04a16-9b2f-11eb-8b86-12bb97331649.png

編輯:jq

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • JAVA
    +關(guān)注

    關(guān)注

    19

    文章

    2952

    瀏覽量

    104487
  • SAM
    SAM
    +關(guān)注

    關(guān)注

    0

    文章

    111

    瀏覽量

    33480
  • kill
    +關(guān)注

    關(guān)注

    0

    文章

    9

    瀏覽量

    2093

原文標(biāo)題:還在使用kill -9 pid結(jié)束spring boot項(xiàng)目嗎?不妨試試這幾種優(yōu)雅的方式!

文章出處:【微信號(hào):LinuxHub,微信公眾號(hào):Linux愛(ài)好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    android系統(tǒng)使用appe播放audio資源,相關(guān)進(jìn)程被kill之后appe無(wú)法再次打開(kāi)的原因?

    , appe_exit); 來(lái)解決程序被kill的時(shí)候需要做的資源回收處理,即使用appe_close來(lái)釋放資源。 但是,如果使用kill -9 來(lái)結(jié)束任務(wù),這個(gè)signal是無(wú)法被捕
    發(fā)表于 10-23 07:56

    Spring Cloud Gateway網(wǎng)關(guān)框架

    SpringCloud Gateway功能特征如下: (1) 基于Spring Framework 5, Project Reactor 和 Spring Boot 2.0 進(jìn)行構(gòu)建; (2) 動(dòng)態(tài)路由:能夠匹配任何請(qǐng)求屬性;
    的頭像 發(fā)表于 08-22 09:58 ?413次閱讀
    <b class='flag-5'>Spring</b> Cloud Gateway網(wǎng)關(guān)框架

    SpingBoot的5個(gè)擴(kuò)展點(diǎn),超級(jí)實(shí)用!

    我們?cè)趩?dòng)Spring Boot項(xiàng)目的時(shí)候,是執(zhí)行這樣一個(gè)方法來(lái)啟動(dòng)的
    的頭像 發(fā)表于 02-22 11:28 ?421次閱讀
    SpingBoot的5個(gè)擴(kuò)展點(diǎn),超級(jí)實(shí)用!

    使用Spring Boot 3.2虛擬線程搭建靜態(tài)文件服務(wù)器

    Spring Boot 3.2 于 2023 年 11 月大張旗鼓地發(fā)布,標(biāo)志著 Java 開(kāi)發(fā)領(lǐng)域的一個(gè)關(guān)鍵時(shí)刻。這一突破性的版本引入了一系列革命性的功能。
    的頭像 發(fā)表于 01-09 09:34 ?1072次閱讀
    使用<b class='flag-5'>Spring</b> <b class='flag-5'>Boot</b> 3.2虛擬線程搭建靜態(tài)文件服務(wù)器

    51單片機(jī)for循環(huán)結(jié)束之后還在運(yùn)行

    當(dāng)我們?cè)O(shè)計(jì)并編寫(xiě)嵌入式系統(tǒng)時(shí),使用51單片機(jī)編寫(xiě)程序時(shí),常常會(huì)用到循環(huán)結(jié)構(gòu)來(lái)重復(fù)執(zhí)行一段代碼。然而,有時(shí)我們可能會(huì)遇到51單片機(jī)在循環(huán)結(jié)束之后還在運(yùn)行的問(wèn)題,這可能會(huì)導(dǎo)致系統(tǒng)的功能異?;蛐阅芟陆?/div>
    的頭像 發(fā)表于 12-26 14:06 ?976次閱讀

    Spring事務(wù)失效的十種常見(jiàn)場(chǎng)景

    Spring的聲明式事務(wù)功能更是提供了極其方便的事務(wù)配置方式,配合Spring Boot的自動(dòng)配置,大多數(shù)Spring Boot
    的頭像 發(fā)表于 12-11 15:03 ?836次閱讀

    【飛騰派4G版免費(fèi)試用】Spring Boot和飛騰派融合構(gòu)建的農(nóng)業(yè)物聯(lián)網(wǎng)系統(tǒng)-環(huán)境搭建篇

    ntpdate-u 120.25.108.11) 7.安裝MySQL服務(wù)器(sudo apt install mysql -server) 遇到問(wèn)題: 1測(cè)試中板子TF卡處發(fā)熱較為嚴(yán)重。 下期更新: Spring Boot和飛騰派融合構(gòu)建的農(nóng)業(yè)物聯(lián)網(wǎng)系統(tǒng)-實(shí)現(xiàn)篇1
    發(fā)表于 12-11 15:00

    dubbo和spring cloud區(qū)別

    包括了服務(wù)提供者、注冊(cè)中心、服務(wù)消費(fèi)者等角色。 Spring Cloud是基于Spring Boot的微服務(wù)
    的頭像 發(fā)表于 12-04 14:47 ?1549次閱讀

    為什么不建議用kill-9關(guān)閉程序?

    相信不少Linux運(yùn)維小伙伴在實(shí)際運(yùn)維中經(jīng)常會(huì)用到kill -9 進(jìn)程ID 這個(gè)命令來(lái)干掉程序。實(shí)際真的合理嗎,今天就讓我們一起來(lái)看看為什么技術(shù)牛人都不建議用kill -9關(guān)閉程序,看
    的頭像 發(fā)表于 12-04 09:39 ?2445次閱讀
    為什么不建議用<b class='flag-5'>kill-9</b>關(guān)閉程序?

    如何在Spring Boot應(yīng)用程序中整合ZXing庫(kù)

    在數(shù)字化時(shí)代,二維碼已經(jīng)成為了信息交流的一種常見(jiàn)方式。它們被廣泛用于各種應(yīng)用,從產(chǎn)品標(biāo)簽到活動(dòng)傳單,以及電子支付。本文將向您展示如何在Spring Boot應(yīng)用程序中整合ZXing庫(kù),以創(chuàng)建和解析QR碼。
    的頭像 發(fā)表于 12-03 17:39 ?1048次閱讀

    springboot的優(yōu)點(diǎn)與缺點(diǎn)

    :使用Spring Boot可以快速啟動(dòng)新項(xiàng)目,因?yàn)樗峁┝嗽S多開(kāi)箱即用的組件和功能,例如內(nèi)嵌的Web服務(wù)器(如Tomcat或Undertow)、
    的頭像 發(fā)表于 12-03 15:29 ?1398次閱讀

    Spring Boot 3.2支持虛擬線程和原生鏡像

    Spring Boot 3.2 前幾日發(fā)布,讓我們用 Java 21、GraalVM 和虛擬線程來(lái)嘗試一下。
    的頭像 發(fā)表于 11-30 16:22 ?671次閱讀

    springboot框架介紹

    程。 Spring Boot是由Spring團(tuán)隊(duì)開(kāi)發(fā)并維護(hù)的開(kāi)源項(xiàng)目,它的初衷是為了簡(jiǎn)化Spring應(yīng)用程序的配置和部署。在傳統(tǒng)的
    的頭像 發(fā)表于 11-22 15:53 ?1245次閱讀

    spring分布式框架有哪些

    Spring分布式框架。 Spring Cloud Spring Cloud是基于Spring Boot的分布式開(kāi)發(fā)工具包。它提供了多個(gè)子
    的頭像 發(fā)表于 11-16 10:58 ?735次閱讀

    Spring布能用來(lái)搭建基礎(chǔ)架構(gòu)嗎

    Spring Boot 是一個(gè)用于簡(jiǎn)化 Spring 應(yīng)用程序開(kāi)發(fā)的框架,它利用 Spring 框架的強(qiáng)大功能,使得基礎(chǔ)架構(gòu)的搭建變得更加簡(jiǎn)單、輕量級(jí)、易于維護(hù)。在本文中,我們將詳細(xì)討
    的頭像 發(fā)表于 11-16 10:56 ?362次閱讀