美文网首页
Spring 源码分析 —— 服务优雅关闭

Spring 源码分析 —— 服务优雅关闭

作者: 想起个帅气的头像 | 来源:发表于2021-01-27 23:05 被阅读0次

一. spring服务如何感知关闭信号

我们想停掉一个spring的进程,一般通过kill命令完成,常用的命令如kill -2 pid(ctrl + C)kill -9 pidkill -15 pid

kill -9 可以认为操作系统从内核级别直接强行kill进程,对服务来说没有任何的准备时间和清场时间,直接被kill掉,且无法被监听。
kill -2 和 -15 则是操作系统给该进程发送一个信号通知,告知应用主动关闭,应用可以监听并接收到信号,可以完成一些关闭回收等动作,然后自我停止。

准确来说,这些关闭信号并不是由spring感知,而是由java线程Signal Dispatcher监听,此线程将收到的信号交给JVM,JVM判断信号种类,如果是-2/-15等关闭类型,则交由java.lang.Shutdown完成关闭,关闭前会触发所有的shutdown hook。

Runtime.getRuntime().addShutdownHook()可以添加自定义的shutdown hook。

关于jvm关闭详细的原理分析过程请参考 java进程关闭事件监听

二. spring关闭进程源码分析

spring在启动过程中也是通过Runtime.getRuntime().addShutdownHook()来注册hook回调。

SpringApplication

private void refreshContext(ConfigurableApplicationContext context) {
  //registerShutdownHook 默认true
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        }
        catch (AccessControlException ex) {
            // Not allowed in some environments.
        }
    }
    refresh((ApplicationContext) context);
}

SpringApplicationrun方法中,会调用refreshContext,默认情况下会调用context.registerShutdownHook();

AbstractApplicationContext

@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);
    }
}

AbstractApplicationContext. registerShutdownHook(),实现了添加shutdown hook,对应的回调实现为doClose();

/**
 * Actually performs context closing: publishes a ContextClosedEvent and
 * destroys the singletons in the bean factory of this application context.
 * <p>Called by both {@code close()} and a JVM shutdown hook, if any.
 * @see org.springframework.context.event.ContextClosedEvent
 * @see #destroyBeans()
 * @see #close()
 * @see #registerShutdownHook()
 */
protected void doClose() {
    // Check whether an actual close attempt is necessary...
    if (this.active.get() && this.closed.compareAndSet(false, true)) {
        if (logger.isDebugEnabled()) {
        logger.debug("Closing " + this);
        }
        // 注销所有的MBean
        LiveBeansView.unregisterApplicationContext(this);

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

        // Stop all Lifecycle beans, to avoid delays during individual destruction.
        // 调用所有lifecycle子类bean的关闭方法
        if (this.lifecycleProcessor != null) {
            try {
                this.lifecycleProcessor.onClose();
            }
            catch (Throwable ex) {
                logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
            }
        }

        // Destroy all cached singletons in the context's BeanFactory.
        // 清空上下文缓存的各类singleton bean
        destroyBeans();

        // Close the state of this context itself.
        // 将DefaultListableBeanFactory 置为null
        closeBeanFactory();

        // Let subclasses do some final clean-up if they wish...
        // 调用子类扩展的onClose
        onClose();

        // Reset local application listeners to pre-refresh state.
        // 重置applicationListeners 为预刷新状态
        if (this.earlyApplicationListeners != null) {
            this.applicationListeners.clear();
            this.applicationListeners.addAll(this.earlyApplicationListeners);
        }

        // Switch to inactive.
        // 设置运行标示为false
        this.active.set(false);
    }
}
  1. 调用unregisterApplicationContext注销所有的MBean
  2. 发布ContextClosedEvent事件
  3. 清空上下文缓存的各类singleton bean。
  4. 将beanFactory 置为 null
  5. 调用子类的onClose方法,onClose为上下文子类提供一个实现自我扩展的机制。
  6. 重置applicationListeners
  7. 设置active标示为false

其中发布ContextClosedEvent事件是非常关键的一步,所有希望完成清场操作的业务或组件都可以监听此事件来完成触发。

整个回收过程还是比较简单的。

相关文章

网友评论

      本文标题:Spring 源码分析 —— 服务优雅关闭

      本文链接:https://www.haomeiwen.com/subject/xcinzktx.html