【SpringBoot】服務對註冊中心的下線時機

酷酷-發表於2024-06-27

1 前言

上節我們主要看了下服務啟動的註冊時機,可以看到它最後的落點是在例項化 DiscoveryClient 的時候進行服務的註冊,看完啟動註冊,那麼我們本節就看看當服務關閉時候的一個下線時機以及過程。

當然服務關閉也分情況,比如我能想到的直接暴力關閉類似 kill -9,柔和優雅關閉的類似 kill -15,我們本節主要看柔和關閉的情況。

2 下線時機

服務的下線時機,我看了下大概分為兩個地方,但是都是由同一個入口進來的。

同一個入口就是我們 SpringBoot 服務啟動的時候,重新整理上下文裡註冊的關閉鉤子函式,它會在 JVM 退出的時候,來得到執行。

兩個地方:

(1)EurekaAutoServiceRegistration 實現了 SmartLifecycle 生命週期的介面,stop 方法會得到執行

(2)DiscoveryClient 的 destoryMethod = "shutdown" 銷燬的時候得到執行

2.1 一個入口

// ###SpringApplication
private void refreshContext(ConfigurableApplicationContext context) {
    // 上下文的重新整理
    refresh(context);
    // 註冊關閉鉤子
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        }
        catch (AccessControlException ex) {
            // Not allowed in some environments.
        }
    }
}
// ### AbstractApplicationContext
@Override
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);
    }
}
// ###
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);
        }
        if (!NativeDetector.inNativeImage()) {
            LiveBeansView.unregisterApplicationContext(this);
        }
        try {
            // 釋出關閉事件 Publish shutdown event.
            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.
        if (this.lifecycleProcessor != null) {
            try {
                // 兩個地方之一 生命週期的關閉是在這裡執行的
                this.lifecycleProcessor.onClose();
            }
            catch (Throwable ex) {
                logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
            }
        }
        // 銷燬bean Destroy all cached singletons in the context's BeanFactory. 兩個地方之二 Bean的銷燬是在這裡執行的
        destroyBeans();
        // 關閉bean工廠 Close the state of this context itself.
        closeBeanFactory();
        // 這裡SpringBoot 用於關閉web容器比如停止掉tomcat Let subclasses do some final clean-up if they wish...
        onClose();
        // Reset local application listeners to pre-refresh state.
        if (this.earlyApplicationListeners != null) {
            this.applicationListeners.clear();
            this.applicationListeners.addAll(this.earlyApplicationListeners);
        }
        // Switch to inactive.
        this.active.set(false);
    }
}

2.2 兩個地方

(1)EurekaAutoServiceRegistration 實現了 SmartLifecycle 生命週期的介面,stop 方法會得到執行

// ### EurekaAutoServiceRegistration
public void stop() {
    this.serviceRegistry.deregister(this.registration);
    this.running.set(false);
}

(2)DiscoveryClient 的 destoryMethod = "shutdown" 銷燬的時候得到執行

// ### CloudEurekaClient
// public class CloudEurekaClient extends DiscoveryClient
@Bean(
    destroyMethod = "shutdown"
)
@ConditionalOnMissingBean(
    value = {EurekaClient.class},
    search = SearchStrategy.CURRENT
)
public EurekaClient eurekaClient(ApplicationInfoManager manager, EurekaClientConfig config) {
    return new CloudEurekaClient(manager, config, this.optionalArgs, this.context);
}

日誌資訊:

最後畫個圖,捋一下思路:

3 小結

好啦,本節下線的時機就看到這裡,有理解不對的地方歡迎指正。

相關文章