linux java 程序启动会自动关闭_常见Java应用如何优雅关闭

d71a3e540ea0fd8062e9284d94f8494e.png
作者:卢利如
来源:点我达技术 http:// tech.dianwoda.com

一、前言

在我们进行系统升级的时候,往往需要关闭我们的应用,然后重启。在关闭应用前,我们希望做一些前置操作,比如关闭数据库、redis连接,清理zookeeper的临时节点,释放分布式锁,持久化缓存数据等等。

二、Linux的信号机制

在linux上,我们关闭进程主要是使用kill <pid>的方式。

当执行该命令以后,linux会向进程发送一个信号,进程收到以后之后,可以做一些清理工作。

kill命令默认的信号值为15,即SIGTERM信号。

通过kill -l查看linux支持哪些信号:

14246b8dc39598c5ffb962a6ddf33fc8.png

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引发问题,降低框架的使用难度。

推荐阅读(点击即可跳转阅读)

1. SpringBoot内容聚合

2. 面试题内容聚合

3. 设计模式内容聚合

4. Mybatis内容聚合

5. 多线程内容聚合

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值