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

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

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

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

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

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

講的這個復雜,簡單點來說就是用來殺死linux中的進程,啥?你問我啥是進程?請自行百度。

我相信很多人都用過kill -9 pid 這個命令,徹底殺死進程的意思,一般情況我們使用它沒有上面問題,但是在我們項目中使用它就有可能存在致命的問題。

kill -9 pid 帶來的問題

由于kill -9 屬于暴力刪除,所以會給程序帶來比較嚴重的后果,那究竟會帶來什么后果呢?

舉個栗子:轉(zhuǎn)賬功能,再給兩個賬戶進行加錢扣錢的時候突然斷電了?這個時候會發(fā)生什么事情?對于InnoDB存儲引擎來說,沒有什么損失,因為它支持事務(wù),但是對于MyISAM引擎來說那簡直就是災(zāi)難,為什么?假如給A賬戶扣了錢,現(xiàn)在需要將B賬戶加錢,這個時候停電了,就會造成,A的錢被扣了,但是B沒有拿到這筆錢,這在生產(chǎn)環(huán)境是絕對不允許的,kill -9 相當于突然斷電的效果。

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

在MyISAM引擎中表現(xiàn)的更明顯,比如用戶的信息由兩張表維護,管理員修改用戶信息的時候需要修改兩張表,但由于你的kill -9 暴力結(jié)束項目,導致只修改成功了一張表,這也會導致數(shù)據(jù)的不一致性,這是小事,因為大不了再修改一次,但是金錢、合同這些重要的信息如果由于你的暴力刪除導致錯亂,我覺得可能比刪庫跑路還嚴重,至少刪庫還能恢復,你這個都不知道錯在哪里。

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

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

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

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

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

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

第四步:停止容器。

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

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

kill -15 pid

這種方式也會比較優(yōu)雅的結(jié)束進程(項目),使用他的時候需要慎重,為什么呢?我們來看個例子

我寫了一個普通的controller方法做測試

@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”; }

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

application.yml

server: port: 9988

啟動項目

sudo mvn spring-boot:run

這是maven啟動springboot項目的方式

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

看到這個就代表項目啟動成了

找到項目的進程id

sudo ps -ef |grep shutdown

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

這個就是項目的進程號,接下來我們先測試test接口,讓線程進入休眠狀態(tài),然后再使用kill -15 14086停止項目

sudo curl 127.0.0.1:9988/test

回到項目日志

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

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

pIYBAGB1UQuActwnAASaBSQr8kc513.png

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

ConfigurableApplicationContext colse

我們先看怎么實現(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”; } /** * 停機 */ @PostMapping(value = “shutdown”) public void shutdown(){ ConfigurableApplicationContext cyx = (ConfigurableApplicationContext) context; cyx.close(); } }

重點在:cyx.close();,為什么他能停止springboot項目呢?請看源碼

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

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

我們看測試結(jié)果

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

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

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

actuator

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

引入依賴

《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

我這里對actuator的接口重新給定了一個接口,這樣可提高安全性,下面我們來測試一下

@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”; }

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

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

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

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

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

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

1.新增停止springboot服務(wù)類: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(“請嘗試暴力關(guān)閉”); } } catch (InterruptedException ex) { System.out.println(“異常了”); Thread.currentThread().interrupt(); } } } }

2.在啟動類中加入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; } }

這樣我們就配置好了,我們再來測試一遍,test的接口還是休眠10秒

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

我們發(fā)現(xiàn)這次沒有報錯了,他是等待了一段時間之后再結(jié)束的線程池,這個時間就是我們在ElegantShutdownConfig類中配置的waitTime。

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

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

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

Destroy:消滅、毀滅

pre:前綴縮寫

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

新增服務(wù)停止備份工具類: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ù)。。。。。。。。。。?!保? } }

我們再來測試然后打印控制臺日志:

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

編輯:jq

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

    關(guān)注

    0

    文章

    155

    瀏覽量

    12168
  • Test
    +關(guān)注

    關(guān)注

    0

    文章

    80

    瀏覽量

    26918
  • Kill C51
    +關(guān)注

    關(guān)注

    0

    文章

    2

    瀏覽量

    6633
收藏 人收藏

    評論

    相關(guān)推薦

    Spring事務(wù)失效的十種常見場景

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

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

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

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

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

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

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

    Spring Boot 的設(shè)計目標

    什么是Spring Boot Spring BootSpring 開源組織下的一個子項目,
    的頭像 發(fā)表于 10-13 14:56 ?486次閱讀
    <b class='flag-5'>Spring</b> <b class='flag-5'>Boot</b> 的設(shè)計目標

    Spring Boot的啟動原理

    可能很多初學者會比較困惑,Spring Boot 是如何做到將應(yīng)用代碼和所有的依賴打包成一個獨立的 Jar 包,因為傳統(tǒng)的 Java 項目打包成 Jar 包之后,需要通過 -classpath 屬性
    的頭像 發(fā)表于 10-13 11:44 ?541次閱讀
    <b class='flag-5'>Spring</b> <b class='flag-5'>Boot</b>的啟動原理

    Spring Boot啟動 Eureka流程

    在上篇中已經(jīng)說過了 Eureka-Server 本質(zhì)上是一個 web 應(yīng)用的項目,今天就來看看 Spring Boot 是怎么啟動 Eureka 的。 Spring
    的頭像 發(fā)表于 10-10 11:40 ?707次閱讀
    <b class='flag-5'>Spring</b> <b class='flag-5'>Boot</b>啟動 Eureka流程

    Spring Boot Actuator快速入門

    不知道大家在寫 Spring Boot 項目的過程中,使用過 Spring Boot Actuator 嗎?知道
    的頭像 發(fā)表于 10-09 17:11 ?527次閱讀

    Spring Boot時配置JSON序列化選項的幾種方法

    Spring Boot Web 項目中,當使用JSON格式接收數(shù)據(jù)和返回數(shù)據(jù)的時候,Spring Boot 默認使用一個ObjectMap
    的頭像 發(fā)表于 10-09 10:26 ?1815次閱讀

    Spring Boot配置加載相關(guān)知識

    Spring BOOT 啟動參數(shù) 在Java Web的開發(fā)完成后,以前我們都會打包成war文件,然后放大web容器,比如tomcat、jetty這樣的容器?,F(xiàn)在基于SpringBoot開發(fā)的項目
    的頭像 發(fā)表于 10-07 15:47 ?394次閱讀

    Spring Boot如何實現(xiàn)異步任務(wù)

    Spring Boot 提供了多種方式來實現(xiàn)異步任務(wù),這里介紹三種主要實現(xiàn)方式。 1、基于注解 @Async @Async 注解是 Spring 提供的一種輕量級異步方法實現(xiàn)方式,它可以標記在方法上
    的頭像 發(fā)表于 09-30 10:32 ?1135次閱讀

    Gradle構(gòu)建的多模塊Spring Boot項目

    構(gòu)建的多模塊 Spring Boot 項目,JDK 版本為8,IDEA 版本為 2022.3.1 。 1. 創(chuàng)建項目 打開IDEA,選擇菜單:File -> New -> Proje
    的頭像 發(fā)表于 09-25 14:46 ?2460次閱讀
    Gradle構(gòu)建的多模塊<b class='flag-5'>Spring</b> <b class='flag-5'>Boot</b><b class='flag-5'>項目</b>

    Spring Boot Starter需要些什么

    pulsar-spring-boot-starter是非常有必要的,在此之前,我們先看看一個starter需要些什么。 Spring Boot Starter spring-boot
    的頭像 發(fā)表于 09-25 11:35 ?632次閱讀
    <b class='flag-5'>Spring</b> <b class='flag-5'>Boot</b> Starter需要些什么

    kafka client在 spring如何實現(xiàn)

    認識了 spring-boot-starter ,今天不妨來看下如何寫一個 pulsar-spring-boot-starter 模塊。 目標 寫一個完整的類似 kafka-spring-boot-st
    的頭像 發(fā)表于 09-25 11:21 ?399次閱讀
    kafka client在 <b class='flag-5'>spring</b>如何實現(xiàn)

    Spring Boot虛擬線程和Webflux性能對比

    早上看到一篇關(guān)于Spring Boot虛擬線程和Webflux性能對比的文章,覺得還不錯。內(nèi)容較長,抓重點給大家介紹一下這篇文章的核心內(nèi)容,方便大家快速閱讀。
    發(fā)表于 09-24 14:54 ?753次閱讀
    <b class='flag-5'>Spring</b> <b class='flag-5'>Boot</b>虛擬線程和Webflux性能對比