Spring Boot应用关闭分析

优质博文:IT-BLOG-CN

一、使用spring容器的close方法关闭。

可通过在代码中获取SpringContext并调用close方法去关闭容器。

使用SpringApplication的exit方法。

public static int exit(ApplicationContext context,
            ExitCodeGenerator... exitCodeGenerators) {
        Assert.notNull(context, "Context must not be null");
        int exitCode = 0;
        try {
            try {
                //获取ExitCodeGenerator的Bean并用ExitCodeGenerators管理
                ExitCodeGenerators generators = new ExitCodeGenerators();
                Collection<ExitCodeGenerator> beans = context
                        .getBeansOfType(ExitCodeGenerator.class).values();
                generators.addAll(exitCodeGenerators);
                generators.addAll(beans);
                exitCode = generators.getExitCode();
                if (exitCode != 0) {
                    // 发布ExitCodeEvent事件
                    context.publishEvent(new ExitCodeEvent(context, exitCode));
                }
            }
            finally {
                close(context);
            }
        }
        catch (Exception ex) {
            ex.printStackTrace();
            exitCode = (exitCode != 0) ? exitCode : 1;
        }
        return exitCode;
    }

上述代码,总的来说就是,获取ExitCodeGenerator的Bean并用ExitCodeGenerators管理, 注意getExitCode()的实现是取ExitCodeGenerator集合中最大的exitCode作为最终exitCode,

最后,关闭容器。

二、exitCode

SpringApplication#exit方法返回的exitCode还需要自行调用System#exit方法去指定。 该System#exit(int code)的参数,能被父进程获取并使用。一般按照惯例0为程序正常退出,非0位不正常退出。 我写了的运行demo:

@Slf4j
@SpringBootApplication
public class ApplicationMainShutDownBySpringApplication {

    public static void main(String[] args) {

        ConfigurableApplicationContext ctx = new SpringApplicationBuilder(ApplicationMainShutDownBySpringApplication.class).build().run(args);

        int exitCode = SpringApplication.exit(ctx);
        log.info("exitCode is {}!", exitCode);
        System.exit(exitCode);
    }

    @Bean
    public ExitCodeGenerator exitCodeGenerator() {
        return () -> 10;
    }
}


执行window bat:
java -jar spring-boot-mvc-shutdown-demo.jar & echo %ERRORLEVEL%

省略其他日志最终输出:
10

可以看最终输出是:10。

二、使用Actuator的shutdown http接口或JMX

可参考Actuator包的ShutdownEndpoint,实质上是调用spring容器的close方法关闭的。

http方式关闭:
在这里插入图片描述

JMX方式关闭:
在这里插入图片描述

三、kill进程

一般的kill(kill -15)会触发应用在refreshContext时(并且SpringApplication实例的registerShutdownHook为true时)加上的注册到JVM的shutdownhook。

public void registerShutdownHook() {
    if (this.shutdownHook == null) {
        // No shutdown hook registered yet.
        this.shutdownHook = new Thread() {
            @Override
            public void run() {
                synchronized (startupShutdownMonitor) {
                    doClose();
                }
            }
        };
        Runtime.getRuntime().addShutdownHook(this.shutdownHook);
    }
}

四、pidFile生成

spring boot提供ApplicationPidFileWriter类,将运行时pid写入指定文件中。
应用场景:可供kill使用。kill $(cat application.pid)

添加步骤:

【1】增加监听器

org.springframework.context.ApplicationListener=

org.springframework.boot.context.ApplicationPidFileWriter

【2】配置项

spring:
pid:
 fail-on-write-error: true
 #可通过此文件里的pid去关闭应用
 file: ./application.pid

五、spring容器close代码分析

这里对容器关闭进行一些分析,以注释的形式写在下面。

    /**
     * Close this application context, destroying all beans in its bean factory.
     * <p>Delegates to {@code doClose()} for the actual closing procedure.
     * Also removes a JVM shutdown hook, if registered, as it's not needed anymore.
     * @see #doClose()
     * @see #registerShutdownHook()
     */
    @Override
    public void close() {
        synchronized (this.startupShutdownMonitor) {
            //委托给钩子方法doClose去做
            doClose();
            // If we registered a JVM shutdown hook, we don't need it anymore now:
            // We've already explicitly closed the context.
            if (this.shutdownHook != null) {
                try {
                    //去掉shutdown hook
                    Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
                }
                catch (IllegalStateException ex) {
                    // ignore - VM is already shutting down
                }
            }
        }
    }
    protected void doClose() {
        // Check whether an actual close attempt is necessary...
        if (this.active.get() && this.closed.compareAndSet(false, true)) {
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值