一. spring服务如何感知关闭信号
我们想停掉一个spring的进程,一般通过kill命令完成,常用的命令如kill -2 pid(ctrl + C)、kill -9 pid、kill -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);
}
在SpringApplication
的run
方法中,会调用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);
}
}
- 调用unregisterApplicationContext注销所有的MBean
- 发布ContextClosedEvent事件
- 清空上下文缓存的各类singleton bean。
- 将beanFactory 置为 null
- 调用子类的onClose方法,onClose为上下文子类提供一个实现自我扩展的机制。
- 重置applicationListeners
- 设置active标示为false
其中发布ContextClosedEvent事件是非常关键的一步,所有希望完成清场操作的业务或组件都可以监听此事件来完成触发。
整个回收过程还是比较简单的。