精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

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

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

3天內不再提示

求求你們別再用kill -9了,這才是Spring Boot停機的正確方式!

jf_ro2CN3Fa ? 來源:CSDN ? 2023-05-15 14:56 ? 次閱讀


再談為了提醒明知故犯(在一坑里迭倒兩次不是不多見),由于業務系統中大量使用了spring Boot embedded tomcat的模式運行,在一些運維腳本中經常看到Linuxkill 指令,然而它的使用也有些講究,要思考如何能做到優雅停機。

何為優雅關機

就是為確保應用關閉時,通知應用進程釋放所占用的資源

  • 線程池,shutdown(不接受新任務等待處理完)還是shutdownNow(調用 Thread.interrupt進行中斷)
  • socket 鏈接,比如:netty、mq
  • 告知注冊中心快速下線(靠心跳機制客服早都跳起來了),比如:eureka
  • 清理臨時文件,比如:poi
  • 各種堆內堆外內存釋放

總之,進程強行終止會帶來數據丟失或者終端無法恢復到正常狀態,在分布式環境下還可能導致數據不一致的情況。

基于 Spring Boot + MyBatis Plus + Vue & Element 實現的后臺管理系統 + 用戶小程序,支持 RBAC 動態權限、多租戶、數據權限、工作流、三方登錄、支付、短信、商城等功能

  • 項目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 視頻教程:https://doc.iocoder.cn/video/

kill指令

kill -9 pid 可以模擬了一次系統宕機,系統斷電等極端情況,而kill -15 pid 則是等待應用關閉,執行阻塞操作,有時候也會出現無法關閉應用的情況(線上理想情況下,是bug就該尋根溯源)

#查看jvm進程pid
jps
#列出所有信號名稱
kill-l


>基于SpringCloudAlibaba+Gateway+Nacos+RocketMQ+Vue&Element實現的后臺管理系統+用戶小程序,支持RBAC動態權限、多租戶、數據權限、工作流、三方登錄、支付、短信、商城等功能
>
>*項目地址://github.com/YunaiV/yudao-cloud>
>*視頻教程//doc.iocoder.cn/video/>

#Windows下信號常量值
#簡稱全稱數值
#INTSIGINT2Ctrl+C中斷
#ILLSIGILL4非法指令
#FPESIGFPE8floatingpointexception(浮點異常)
#SEGVSIGSEGV11segmentviolation(段錯誤)
#TERMSIGTERM5Softwareterminationsignalfromkill(Kill發出的軟件終止)
#BREAKSIGBREAK21Ctrl-Breaksequence(Ctrl+Break中斷)
#ABRTSIGABRT22abnormalterminationtriggeredbyabortcall(Abort)

#linux信號常量值
#簡稱全稱數值
#HUPSIGHUP1終端斷線
#INTSIGINT2中斷(同Ctrl+C)
#QUITSIGQUIT3退出(同Ctrl+)
#KILLSIGKILL9強制終止
#TERMSIGTERM15終止
#CONTSIGCONT18繼續(與STOP相反,fg/bg命令)
#STOPSIGSTOP19暫停(同Ctrl+Z)
#....

#可以理解為操作系統從內核級別強行殺死某個進程
kill-9pid
#理解為發送一個通知,等待應用主動關閉
kill-15pid
#也支持信號常量值全稱或簡寫(就是去掉SIG后)
kill-lKILL

思考:jvm是如何接受處理linux信號量的?

當然是在jvm啟動時就加載了自定義SignalHandler,關閉jvm時觸發對應的handle。

publicinterfaceSignalHandler{
SignalHandlerSIG_DFL=newNativeSignalHandler(0L);
SignalHandlerSIG_IGN=newNativeSignalHandler(1L);

voidhandle(Signalvar1);
}
classTerminator{
privatestaticSignalHandlerhandler=null;

Terminator(){
}
//jvm設置SignalHandler,在System.initializeSystemClass中觸發
staticvoidsetup(){
if(handler==null){
SignalHandlervar0=newSignalHandler(){
publicvoidhandle(Signalvar1){
Shutdown.exit(var1.getNumber()+128);//調用Shutdown.exit
}
};
handler=var0;

try{
Signal.handle(newSignal("INT"),var0);//中斷時
}catch(IllegalArgumentExceptionvar3){
;
}

try{
Signal.handle(newSignal("TERM"),var0);//終止時
}catch(IllegalArgumentExceptionvar2){
;
}

}
}
}

Runtime.addShutdownHook

在了解Shutdown.exit之前,先看Runtime.getRuntime().addShutdownHook(shutdownHook);則是為jvm中增加一個關閉的鉤子,當jvm關閉的時候調用。

publicclassRuntime{
publicvoidaddShutdownHook(Threadhook){
SecurityManagersm=System.getSecurityManager();
if(sm!=null){
sm.checkPermission(newRuntimePermission("shutdownHooks"));
}
ApplicationShutdownHooks.add(hook);
}
}
classApplicationShutdownHooks{
/*Thesetofregisteredhooks*/
privatestaticIdentityHashMaphooks;
staticsynchronizedvoidadd(Threadhook){
if(hooks==null)
thrownewIllegalStateException("Shutdowninprogress");

if(hook.isAlive())
thrownewIllegalArgumentException("Hookalreadyrunning");

if(hooks.containsKey(hook))
thrownewIllegalArgumentException("Hookpreviouslyregistered");

hooks.put(hook,hook);
}
}
//它含數據結構和邏輯管理虛擬機關閉序列
classShutdown{
/*Shutdown系列狀態*/
privatestaticfinalintRUNNING=0;
privatestaticfinalintHOOKS=1;
privatestaticfinalintFINALIZERS=2;
privatestaticintstate=RUNNING;
/*是否應該運行所以finalizers來exit?*/
privatestaticbooleanrunFinalizersOnExit=false;
//系統關閉鉤子注冊一個預定義的插槽.
//關閉鉤子的列表如下:
//(0)Consolerestorehook
//(1)Applicationhooks
//(2)DeleteOnExithook
privatestaticfinalintMAX_SYSTEM_HOOKS=10;
privatestaticfinalRunnable[]hooks=newRunnable[MAX_SYSTEM_HOOKS];
//當前運行關閉鉤子的鉤子的索引
privatestaticintcurrentRunningHook=0;
/*前面的靜態字段由這個鎖保護*/
privatestaticclassLock{};
privatestaticObjectlock=newLock();

/*為nativehalt方法提供鎖對象*/
privatestaticObjecthaltLock=newLock();

staticvoidadd(intslot,booleanregisterShutdownInProgress,Runnablehook){
synchronized(lock){
if(hooks[slot]!=null)
thrownewInternalError("Shutdownhookatslot"+slot+"alreadyregistered");

if(!registerShutdownInProgress){//執行shutdown過程中不添加hook
if(state>RUNNING)//如果已經在執行shutdown操作不能添加hook
thrownewIllegalStateException("Shutdowninprogress");
}else{//如果hooks已經執行完畢不能再添加hook。如果正在執行hooks時,添加的槽點小于當前執行的槽點位置也不能添加
if(state>HOOKS||(state==HOOKS&&slot<=?currentRunningHook))
????????????????????thrownewIllegalStateException("Shutdowninprogress");
}

hooks[slot]=hook;
}
}
/*執行所有注冊的hooks
*/
privatestaticvoidrunHooks(){
for(inti=0;itry{
Runnablehook;
synchronized(lock){
//acquirethelocktomakesurethehookregisteredduring
//shutdownisvisiblehere.
currentRunningHook=i;
hook=hooks[i];
}
if(hook!=null)hook.run();
}catch(Throwablet){
if(tinstanceofThreadDeath){
ThreadDeathtd=(ThreadDeath)t;
throwtd;
}
}
}
}
/*關閉JVM的操作
*/
staticvoidhalt(intstatus){
synchronized(haltLock){
halt0(status);
}
}
//JNI方法
staticnativevoidhalt0(intstatus);
//shutdown的執行順序:runHooks>runFinalizersOnExit
privatestaticvoidsequence(){
synchronized(lock){
/*Guardagainstthepossibilityofadaemonthreadinvokingexit
*afterDestroyJavaVMinitiatestheshutdownsequence
*/
if(state!=HOOKS)return;
}
runHooks();
booleanrfoe;
synchronized(lock){
state=FINALIZERS;
rfoe=runFinalizersOnExit;
}
if(rfoe)runAllFinalizers();
}
//Runtime.exit時執行,runHooks>runFinalizersOnExit>halt
staticvoidexit(intstatus){
booleanrunMoreFinalizers=false;
synchronized(lock){
if(status!=0)runFinalizersOnExit=false;
switch(state){
caseRUNNING:/*Initiateshutdown*/
state=HOOKS;
break;
caseHOOKS:/*Stallandhalt*/
break;
caseFINALIZERS:
if(status!=0){
/*Haltimmediatelyonnonzerostatus*/
halt(status);
}else{
/*Compatibilitywitholdbehavior:
*Runmorefinalizersandthenhalt
*/
runMoreFinalizers=runFinalizersOnExit;
}
break;
}
}
if(runMoreFinalizers){
runAllFinalizers();
halt(status);
}
synchronized(Shutdown.class){
/*Synchronizeontheclassobject,causinganyotherthread
*thatattemptstoinitiateshutdowntostallindefinitely
*/
sequence();
halt(status);
}
}
//shutdown操作,與exit不同的是不做halt操作(關閉JVM)
staticvoidshutdown(){
synchronized(lock){
switch(state){
caseRUNNING:/*Initiateshutdown*/
state=HOOKS;
break;
caseHOOKS:/*Stallandthenreturn*/
caseFINALIZERS:
break;
}
}
synchronized(Shutdown.class){
sequence();
}
}
}

spring 3.2.12

在spring中通過ContextClosedEvent事件來觸發一些動作(可以拓展),主要通過LifecycleProcessor.onClose來做stopBeans。由此可見spring也基于jvm做了拓展。

publicabstractclassAbstractApplicationContextextendsDefaultResourceLoader{
publicvoidregisterShutdownHook(){
if(this.shutdownHook==null){
//Noshutdownhookregisteredyet.
this.shutdownHook=newThread(){
@Override
publicvoidrun(){
doClose();
}
};
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
}
}
protectedvoiddoClose(){
booleanactuallyClose;
synchronized(this.activeMonitor){
actuallyClose=this.active&&!this.closed;
this.closed=true;
}

if(actuallyClose){
if(logger.isInfoEnabled()){
logger.info("Closing"+this);
}

LiveBeansView.unregisterApplicationContext(this);

try{
//發布應用內的關閉事件
publishEvent(newContextClosedEvent(this));
}
catch(Throwableex){
logger.warn("ExceptionthrownfromApplicationListenerhandlingContextClosedEvent",ex);
}

//停止所有的Lifecyclebeans.
try{
getLifecycleProcessor().onClose();
}
catch(Throwableex){
logger.warn("ExceptionthrownfromLifecycleProcessoroncontextclose",ex);
}

//銷毀spring的BeanFactory可能會緩存單例的Bean.
destroyBeans();

//關閉當前應用上下文(BeanFactory)
closeBeanFactory();

//執行子類的關閉邏輯
onClose();

synchronized(this.activeMonitor){
this.active=false;
}
}
}
}
publicinterfaceLifecycleProcessorextendsLifecycle{
/**
*Notificationofcontextrefresh,e.g.forauto-startingcomponents.
*/
voidonRefresh();

/**
*Notificationofcontextclosephase,e.g.forauto-stoppingcomponents.
*/
voidonClose();
}

spring boot

到這里就進入重點了,spring boot中有spring-boot-starter-actuator 模塊提供了一個 restful 接口,用于優雅停機。執行請求 curl -X POST http://127.0.0.1:8088/shutdown ,待關閉成功則返回提示。

注:線上環境該url需要設置權限,可配合 spring-security使用或在nginx中限制內網訪問

#啟用shutdown
endpoints.shutdown.enabled=true
#禁用密碼驗證
endpoints.shutdown.sensitive=false
#可統一指定所有endpoints的路徑
management.context-path=/manage
#指定管理端口和IP
management.port=8088
management.address=127.0.0.1

#開啟shutdown的安全驗證(spring-security)
endpoints.shutdown.sensitive=true
#驗證用戶名
security.user.name=admin
#驗證密碼
security.user.password=secret
#角色
management.security.role=SUPERUSER

spring boot的shutdown原理也不復雜,其實還是通過調用AbstractApplicationContext.close實現的。

@ConfigurationProperties(
prefix="endpoints.shutdown"
)
publicclassShutdownMvcEndpointextendsEndpointMvcAdapter{
publicShutdownMvcEndpoint(ShutdownEndpointdelegate){
super(delegate);
}
//post請求
@PostMapping(
produces={"application/vnd.spring-boot.actuator.v1+json","application/json"}
)
@ResponseBody
publicObjectinvoke(){
return!this.getDelegate().isEnabled()?newResponseEntity(Collections.singletonMap("message","Thisendpointisdisabled"),HttpStatus.NOT_FOUND):super.invoke();
}
}
@ConfigurationProperties(
prefix="endpoints.shutdown"
)
publicclassShutdownEndpointextendsAbstractEndpoint<Map<String,Object>>implementsApplicationContextAware{
privatestaticfinalMapNO_CONTEXT_MESSAGE=Collections.unmodifiableMap(Collections.singletonMap("message","Nocontexttoshutdown."));
privatestaticfinalMapSHUTDOWN_MESSAGE=Collections.unmodifiableMap(Collections.singletonMap("message","Shuttingdown,bye..."));
privateConfigurableApplicationContextcontext;

publicShutdownEndpoint(){
super("shutdown",true,false);
}
//執行關閉
publicMapinvoke(){
if(this.context==null){
returnNO_CONTEXT_MESSAGE;
}else{
booleanvar6=false;

Mapvar1;

classNamelessClass_1implementsRunnable{
NamelessClass_1(){
}

publicvoidrun(){
try{
Thread.sleep(500L);
}catch(InterruptedExceptionvar2){
Thread.currentThread().interrupt();
}
//這個調用的就是AbstractApplicationContext.close
ShutdownEndpoint.this.context.close();
}
}

try{
var6=true;
var1=SHUTDOWN_MESSAGE;
var6=false;
}finally{
if(var6){
Threadthread=newThread(newNamelessClass_1());
thread.setContextClassLoader(this.getClass().getClassLoader());
thread.start();
}
}

Threadthread=newThread(newNamelessClass_1());
thread.setContextClassLoader(this.getClass().getClassLoader());
thread.start();
returnvar1;
}
}
}


審核編輯 :李倩


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

    關注

    0

    文章

    338

    瀏覽量

    14311
  • Boot
    +關注

    關注

    0

    文章

    149

    瀏覽量

    35784
  • kill
    +關注

    關注

    0

    文章

    9

    瀏覽量

    2101

原文標題:求求你們別再用 kill -9 了,這才是 Spring Boot 停機的正確方式!!!

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    Spring Boot如何實現異步任務

    Spring Boot 提供多種方式來實現異步任務,這里介紹三種主要實現方式。 1、基于注解 @Async @Async 注解是
    的頭像 發表于 09-30 10:32 ?1407次閱讀

    啟動Spring Boot項目應用的三種方法

    ,從而使開發人員不再需要定義樣板化的配置。用我的話來理解,就是spring boot其實不是什么新的框架,它默認配置很多框架的使用方式,就像maven整合
    發表于 01-14 17:33

    Spring Boot嵌入式Web容器原理是什么

    ,不需要配置任何特殊的XML配置,為了這個目標,Spring BootSpring 4.0框架之上提供很多特性,幫助應用以“約定優于配置”“開箱即用”的
    發表于 12-16 07:57

    千萬別再用這臺示波器,我怕你會愛上它!

    千萬別再用這臺示波器,我怕你會愛上它!
    發表于 05-30 21:01

    Spring Boot框架錯誤處理

    》 《strong》翻譯《/strong》:雁驚寒《/p》 《/blockquote》《p》《em》摘要:本文通過實例介紹使用Spring Boot在設計API的時候如何正確地對異常
    發表于 09-28 15:31 ?0次下載

    Spring Boot從零入門1 詳述

    在開始學習Spring Boot之前,我之前從未接觸過Spring相關的項目,Java基礎還是幾年前自學的,現在估計也忘得差不多了吧,寫Spring
    的頭像 發表于 12-10 22:18 ?606次閱讀

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

    kill可將指定的信息送至程序。預設的信息為SIGTERM(15),可將指定程序終止。若仍無法終止該程序,可使用SIGKILL(9)信息嘗試強制刪除程序。程序或工作的編號可利用ps指令或jobs指令
    的頭像 發表于 04-13 16:01 ?1351次閱讀
    還在使用<b class='flag-5'>kill</b> -<b class='flag-5'>9</b> pid結束<b class='flag-5'>spring</b> <b class='flag-5'>boot</b>項目嗎?

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

    kill可將指定的信息送至程序。預設的信息為SIGTERM(15),可將指定程序終止。若仍無法終止該程序,可使用SIGKILL(9)信息嘗試強制刪除程序。程序或工作的編號可利用ps指令或jobs指令
    的頭像 發表于 04-13 16:01 ?1507次閱讀
    還在使用<b class='flag-5'>kill</b> -<b class='flag-5'>9</b> pid結束<b class='flag-5'>spring</b> <b class='flag-5'>boot</b>項目嗎?

    Spring Boot特有的實踐

    Spring Boot是最流行的用于開發微服務的Java框架。在本文中,我將與你分享自2016年以來我在專業開發中使用Spring Boot所采用的最佳實踐。這些內容是基于我的個人經驗
    的頭像 發表于 09-29 10:24 ?880次閱讀

    強大的Spring Boot 3.0要來了

    來源:OSC開源社區(ID:oschina2013) Spring Boot 3.0 首個 RC 已發布,此外還為兩個分支發布更新:2.7.5 2.6.13。 3.0.0-RC1: https
    的頭像 發表于 10-31 11:17 ?1741次閱讀

    Spring Boot Web相關的基礎知識

    上一篇文章我們已經學會了如何通過IDEA快速建立一個Spring Boot項目,還介紹Spring Boot項目的結構,介紹
    的頭像 發表于 03-17 15:03 ?625次閱讀

    Spring Boot Actuator快速入門

    一下 Spring Boot Actuator ,學習如何在 Spring Boot 2.x 中使用、配置和擴展這個監控工具。 Spring
    的頭像 發表于 10-09 17:11 ?606次閱讀

    Spring Boot啟動 Eureka流程

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

    Spring Boot的啟動原理

    來指定依賴,才能夠運行。我們今天就來分析講解一下 Spring Boot 的啟動原理。 1. Spring Boot 打包插件 Spring
    的頭像 發表于 10-13 11:44 ?623次閱讀
    <b class='flag-5'>Spring</b> <b class='flag-5'>Boot</b>的啟動原理

    Spring Boot 的設計目標

    ,這樣我們就可以盡快的上手。 使用 Spring Boot 來不僅可以創建基于 war 方式部署的傳統Java應用程序,也可以通過創建獨立的不依賴任何容器(如 tomcat 等)
    的頭像 發表于 10-13 14:56 ?555次閱讀
    <b class='flag-5'>Spring</b> <b class='flag-5'>Boot</b> 的設計目標