这篇文章主要介绍“常见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应用如何关闭”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!