您好,登錄后才能下訂單哦!
這篇文章主要介紹“常見Java應用如何關閉”,在日常操作中,相信很多人在常見Java應用如何關閉問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”常見Java應用如何關閉”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
一、前言
在我們進行系統升級的時候,往往需要關閉我們的應用,然后重啟。在關閉應用前,我們希望做一些前置操作,比如關閉數據庫、redis連接,清理zookeeper的臨時節點,釋放分布式鎖,持久化緩存數據等等。
二、Linux的信號機制
在linux上,我們關閉進程主要是使用 kill 的方式。
當執行該命令以后,linux會向進程發送一個信號,進程收到以后之后,可以做一些清理工作。
kill 命令默認的信號值為 15 ,即 SIGTERM 信號。
通過 kill -l 查看linux支持哪些信號:
linux提供了 signal() api,可以將信號處理函數注冊上去:
#include <signal.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <stdbool.h> static void gracefulClose(int sig) { printf("執行清理工作\n"); printf("JVM 已關閉\n"); exit(0); //正常關閉 } int main(int argc,char *argv[]) { if(signal(SIGTERM,gracefulClose) == SIG_ERR) exit(-1); printf("JVM 已啟動\n"); while(true) { // 執行工作 sleep(1); } }
三、Java提供的Shutdown Hook
Java并不支持類似于linux的信號機制,但是提供了 Runtime.addShutdownHook(Thread hook) 的api。
在JVM關閉前,會并發執行各個Hook線程。
public class ShutdownHook { public static void main(String[] args) throws InterruptedException { Runtime.getRuntime().addShutdownHook(new DbShutdownWork()); System.out.println("JVM 已啟動"); while(true){ Thread.sleep(10L); } } static class DbShutdownWork extends Thread{ public void run(){ System.out.println("關閉數據庫連接"); } } }
四、Spring Boot提供的優雅關閉功能
我們一般采用如下的方式,啟動一個Spring boot應用:
public static void main(String[] args) throws Exception { SpringApplication.run(SampleController.class, args); }
SpringApplication.run()代碼如下,會調用到refreshContext(context)方法:
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; FailureAnalyzers analyzers = null; configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.started(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); Banner printedBanner = printBanner(environment); context = createApplicationContext(); analyzers = new FailureAnalyzers(context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); listeners.finished(context, null); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } return context; } catch (Throwable ex) { handleRunFailure(context, listeners, analyzers, ex); throw new IllegalStateException(ex); } }
refreshContext()方法比較簡單:
private void refreshContext(ConfigurableApplicationContext context) { refresh(context); //調用ApplicationContext.refresh() if (this.registerShutdownHook) { //registerShutdownHook默認值為true try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } }
AbstractApplicationContext.registerShutdownHook()代碼:
public void registerShutdownHook() { if (this.shutdownHook == null) { this.shutdownHook = new Thread() { @Override public void run() { synchronized (startupShutdownMonitor) { doClose(); } } }; Runtime.getRuntime().addShutdownHook(this.shutdownHook); } }
很明顯,Spring boot通過在啟動時,向JVM注冊一個ShutdownHook,從而實現JVM關閉前,正常關閉Spring容器。而Spring在銷毀時,會依次調用bean的destroy動作來銷毀。
五、Dubbo的優雅關閉策略
Dubbo同樣是基于ShutdownHook實現的。
AbstractConfig的static代碼:
static { Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { public void run() { if (logger.isInfoEnabled()) { logger.info("Run shutdown hook now."); } ProtocolConfig.destroyAll(); } }, "DubboShutdownHook")); }
六、總結
只要我們的應用運行在linux平臺上,所有的優雅關閉方案都是基于linux提供的信號機制提供的,JVM也是如此。
Java并沒有為我們提供與之一一對應的api,而是給出了個ShutdownHook機制,也能達到類似的效果,缺點是我們無法得知JVM關閉的原因。
像dubbo、spring boot等成熟的開源框架,都實現了自動注冊ShutdownHook的功能,從而避免使用者忘記調用優雅關閉api引發問題,降低框架的使用難度。
到此,關于“常見Java應用如何關閉”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。