执行kill -2命令你的应用做了哪些事情?

356 阅读5分钟

1.前言

SpringBoot应用在启动的时候会将配置类生成BeanDefinition并注入到容器中、启动Tomcat服务容器,那么当你执行kill -2 pid命令后,你是否知道应用做了哪些操作?如果不知道,请继续往下查看。

2.注册关闭钩子

SpringBoot应用启动的时候会注册关闭钩子

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

通过如上代码可以看到关闭钩子实际上就是一个线程任务

3.执行关闭操作

在启动的时候注册了关闭钩子,那么当执行kill -2 pid命令的时候就会调用注册的钩子,也就是doClose();方法

3.1 发布应用关闭事件

try {
    // Publish shutdown event.
    publishEvent(new ContextClosedEvent(this));
}
catch (Throwable ex) {
    logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
}

如果想在应用关闭的时候做一些资源释放操作,可以通过监听ContextClosedEvent事件来完成

3.2 调用Lifecyclestop()方法

// Stop all Lifecycle beans, to avoid delays during individual destruction.
if (this.lifecycleProcessor != null) {
    try {
        this.lifecycleProcessor.onClose();
    }
    catch (Throwable ex) {
        logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
    }
}

调用所有实现Lifecycle接口的stop()方法,以WebServerGracefulShutdownLifecycle为例

@Override
public void stop(Runnable callback) {
    this.running = false;
    this.webServer.shutDownGracefully((result) -> callback.run());
}
private void doShutdown(GracefulShutdownCallback callback) {
    // 1.获取所有的Connector
    List<Connector> connectors = getConnectors();
    // 2.关闭所有的Connector,使得Tomcat服务容器不在接收web请求
    connectors.forEach(this::close);
    try {
        for (Container host : this.tomcat.getEngine().findChildren()) {
            for (Container context : host.findChildren()) {
                // 3.判断context是否处于激活状态,如果处于激活状态,进行循环判断,直到请求处理完成
                while (isActive(context)) {
                    if (this.aborted) {
                        logger.info("Graceful shutdown aborted with one or more requests still active");
                        callback.shutdownComplete(GracefulShutdownResult.REQUESTS_ACTIVE);
                        return;
                    }
                    Thread.sleep(50);
                }
            }
        }

    }
    catch (InterruptedException ex) {
        Thread.currentThread().interrupt();
    }
    logger.info("Graceful shutdown complete");
    callback.shutdownComplete(GracefulShutdownResult.IDLE);
}

由此部分可以得知WebServerGracefulShutdownLifecycle主要用来实现应用的优雅停机,要求SpringBoot2.3及以上版本

3.3 调用Beandestroy()方法

// Destroy all cached singletons in the context's BeanFactory.
destroyBeans();
protected void destroyBean(String beanName, @Nullable DisposableBean bean) {
    // Actually destroy the bean now...
    if (bean != null) {
        try {
            bean.destroy();
        }
        catch (Throwable ex) {
        }
    }
}

遍历所有的Bean然后调用其实现的destroy();方法,以ThreadPoolTaskExecutor为例

@Override
public void destroy() {
    shutdown();
}
public void shutdown() {
    if (this.executor != null) {
        // 关闭线程池
        if (this.waitForTasksToCompleteOnShutdown) {
            this.executor.shutdown();
        }
        else {
            for (Runnable remainingTask : this.executor.shutdownNow()) {
                cancelRemainingTask(remainingTask);
            }
        }
        awaitTerminationIfNecessary(this.executor);
    }
}

4. 总结

当执行kill -2 pid命令后,应用程序主要发布了ContextClosedEvent事件、调用Connectorclose()方法不再接收web请求并等待处理中的请求执行完成、关闭线程池资源