SC微服务架构下优雅shutdown Stop Bean NacosWatch

SpringCloud微服务Nacos:Failed to stop bean ‘nacosWatch’

在SpringCloud体系下经常我们会集成SpringCloudAlibaba的组件,其中Nacos应该是比较主流的注册配置中心。那么在Springboot2.x集成Nacos客户端在shutdown时会出现timeout问题。

问题现象

当我们对服务进行正常shutdown时,服务内部会受到关闭信号,然后开始依次关闭服务内部的资源。

注意:在关闭服务前需要在注册中心先注销服务。

关闭服务时,NacosWatch问题日志如下所示:

Disconnected from the target VM, address: '127.0.0.1:59632', transport: 'socket'
2022-09-01 08:52:55.221  INFO 26916 --- [extShutdownHook] [ reqId:  ] io.undertow                              : stopping server: Undertow - 2.1.7.Final
2022-09-01 08:52:55.226  INFO 26916 --- [extShutdownHook] [ reqId:  ] io.undertow.servlet                      : Destroying Spring FrameworkServlet 'dispatcherServlet'
2022-09-01 08:52:55.231  INFO 26916 --- [extShutdownHook] [ reqId:  ] o.s.s.c.ThreadPoolTaskScheduler          : Shutting down ExecutorService 'taskScheduler'
2022-09-01 08:52:55.233  WARN 26916 --- [extShutdownHook] [ reqId:  ] o.s.c.support.DefaultLifecycleProcessor  : Failed to stop bean 'nacosWatch'

java.lang.IllegalStateException: UT015023: This Context has been already destroyed
    at io.undertow.servlet.spec.ServletContextImpl.getDeploymentInfo(ServletContextImpl.java:211)
    at io.undertow.servlet.spec.ServletContextImpl.getInitParameterNames(ServletContextImpl.java:438)
    at org.springframework.web.context.support.ServletContextPropertySource.getPropertyNames(ServletContextPropertySource.java:41)
    at com.alibaba.spring.util.PropertySourcesUtils.getPropertyNames(PropertySourcesUtils.java:130)
    at com.alibaba.spring.util.PropertySourcesUtils.getSubProperties(PropertySourcesUtils.java:103)
    at com.alibaba.spring.util.PropertySourcesUtils.getSubProperties(PropertySourcesUtils.java:57)
    at com.alibaba.cloud.nacos.NacosDiscoveryProperties.enrichNacosDiscoveryProperties(NacosDiscoveryProperties.java:616)
    at com.alibaba.cloud.nacos.NacosDiscoveryProperties.getNacosProperties(NacosDiscoveryProperties.java:610)
    at com.alibaba.cloud.nacos.discovery.NacosWatch.stop(NacosWatch.java:166)
    at com.alibaba.cloud.nacos.discovery.NacosWatch.stop(NacosWatch.java:98)
    at org.springframework.context.support.DefaultLifecycleProcessor.doStop(DefaultLifecycleProcessor.java:238)
    at org.springframework.context.support.DefaultLifecycleProcessor.access$300(DefaultLifecycleProcessor.java:53)
    at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.stop(DefaultLifecycleProcessor.java:377)
    at org.springframework.context.support.DefaultLifecycleProcessor.stopBeans(DefaultLifecycleProcessor.java:210)
    at org.springframework.context.support.DefaultLifecycleProcessor.onClose(DefaultLifecycleProcessor.java:128)
    at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1022)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.doClose(ServletWebServerApplicationContext.java:170)
    at org.springframework.context.support.AbstractApplicationContext$1.run(AbstractApplicationContext.java:949)

2022-09-01 08:53:25.240  INFO 26916 --- [extShutdownHook] [ reqId:  ] o.s.c.support.DefaultLifecycleProcessor  : Failed to shut down 1 bean with phase value 0 within timeout of 30000ms: [nacosWatch]
2022-09-01 08:53:25.240  INFO 26916 --- [extShutdownHook] [ reqId:  ] o.s.i.endpoint.EventDrivenConsumer       : Removing {logging-channel-adapter:_org.springframework.integration.errorLogger} as a subscriber to the 'errorChannel' channel
2022-09-01 08:53:25.242  INFO 26916 --- [extShutdownHook] [ reqId:  ] o.s.i.channel.PublishSubscribeChannel    : Channel 'cloudroom-service-cloud-1.errorChannel' has 0 subscriber(s).
2022-09-01 08:53:25.242  INFO 26916 --- [extShutdownHook] [ reqId:  ] o.s.i.endpoint.EventDrivenConsumer       : stopped bean '_org.springframework.integration.errorLogger'
2022-09-01 08:53:25.252  INFO 26916 --- [extShutdownHook] [ reqId:  ] c.a.c.n.registry.NacosServiceRegistry    : De-registering from Nacos Server now...
2022-09-01 08:53:25.252  WARN 26916 --- [extShutdownHook] [ reqId:  ] .s.c.a.CommonAnnotationBeanPostProcessor : Destroy method on bean with name 'nacosAutoServiceRegistration' threw an exception: java.lang.IllegalStateException: UT015023: This Context has been already destroyed
2022-09-01 08:53:25.253 DEBUG 26916 --- [extShutdownHook] [ reqId:  ] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown
2022-09-01 08:53:25.253 DEBUG 26916 --- [extShutdownHook] [ reqId:  ] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans
2022-09-01 08:53:25.254  INFO 26916 --- [extShutdownHook] [ reqId:  ] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
2022-09-01 08:53:25.604 DEBUG 26916 --- [extShutdownHook] [ reqId:  ] h.i.c.PoolingHttpClientConnectionManager : Connection manager is shutting down
2022-09-01 08:53:25.604 DEBUG 26916 --- [extShutdownHook] [ reqId:  ] h.i.c.PoolingHttpClientConnectionManager : Connection manager shut down
2022-09-01 08:53:25.605 DEBUG 26916 --- [nnection_reaper] [ reqId:  ] com.aliyun.oss                           : Shutting down reaper thread.
2022-09-01 08:53:25.605  INFO 26916 --- [extShutdownHook] [ reqId:  ] o.s.s.c.ThreadPoolTaskScheduler          : Shutting down ExecutorService 'taskScheduler'
2022-09-01 08:53:37.948 DEBUG 26916 --- [extShutdownHook] [ reqId:  ] o.s.a.r.c.CachingConnectionFactory       : Closing cached Channel: PublisherCallbackChannelImpl: AMQChannel(amqp://dev@192.168.110.202:5672//dev,1)
2022-09-01 08:53:37.948 DEBUG 26916 --- [extShutdownHook] [ reqId:  ] o.s.a.r.c.PublisherCallbackChannelImpl   : Closing AMQChannel(amqp://dev@192.168.110.202:5672//dev,1)
2022-09-01 08:53:37.956 DEBUG 26916 --- [nectionFactory1] [ reqId:  ] o.s.amqp.rabbit.core.RabbitTemplate      : Removed publisher confirm channel: PublisherCallbackChannelImpl: AMQChannel(amqp://dev@192.168.110.202:5672//dev,1) from map, size now 0
2022-09-01 08:53:37.956 DEBUG 26916 --- [nectionFactory1] [ reqId:  ] o.s.amqp.rabbit.core.RabbitTemplate      : Removed publisher confirm channel: PublisherCallbackChannelImpl: AMQChannel(amqp://dev@192.168.110.202:5672//dev,1) from map, size now 0
2022-09-01 08:53:37.956 DEBUG 26916 --- [nectionFactory1] [ reqId:  ] o.s.a.r.c.PublisherCallbackChannelImpl   : PendingConfirms cleared
2022-09-01 08:53:37.956 DEBUG 26916 --- [nectionFactory2] [ reqId:  ] o.s.a.r.c.PublisherCallbackChannelImpl   : PendingConfirms cleared
2022-09-01 08:53:38.102 DEBUG 26916 --- [extShutdownHook] [ reqId:  ] c.j.y.c.core.util.SpringContextHolder    : 清除SpringContextHolder中的ApplicationContext:org.springframework.boot.web.servlet.context.Annotat****ebServerApplicationContext@19ccca5, started on Thu Sep 01 08:52:17 CST 2022, parent: org.springframework.context.annotatio****gApplicationContext@3954d008
2022-09-01 08:53:38.103  INFO 26916 --- [extShutdownHook] [ reqId:  ] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} closing ...
2022-09-01 08:53:38.114  INFO 26916 --- [extShutdownHook] [ reqId:  ] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} closed
2022-09-01 08:53:38.115 DEBUG 26916 --- [extShutdownHook] [ reqId:  ] o.s.i.monitor.IntegrationMBeanExporter   : Unregistering JMX-exposed beans on shutdown
2022-09-01 08:53:38.116 DEBUG 26916 --- [extShutdownHook] [ reqId:  ] o.s.i.monitor.IntegrationMBeanExporter   : Unregistering JMX-exposed beans
2022-09-01 08:53:38.116  INFO 26916 --- [extShutdownHook] [ reqId:  ] o.s.i.monitor.IntegrationMBeanExporter   : Summary on shutdown: nullChannel
2022-09-01 08:53:38.116  INFO 26916 --- [extShutdownHook] [ reqId:  ] o.s.i.monitor.IntegrationMBeanExporter   : Summary on shutdown: bean 'errorChannel'
2022-09-01 08:53:38.116  INFO 26916 --- [extShutdownHook] [ reqId:  ] o.s.i.monitor.IntegrationMBeanExporter   : Summary on shutdown: bean '_org.springframework.integration.errorLogger.handler' for component '_org.springframework.integration.errorLogger'

Process finished with exit code 130

解决办法

第一步、开启graceful优雅down机配置。(非必要)

bootstrap.yml 配置修改如下 追加配置: server.shutdown: graceful

server:
  port: 8094
  shutdown: graceful

第二步、在代码中注入该Bean,在shutdown时正常关闭NacosWatch

@Slf4j
@Component
public class NacosStopFix implements InitializingBean {

    @Resource
    private NacosWatch nacosWatch;

    @Resource
    private NacosAutoServiceRegistration nacosAutoServiceRegistration;

    @Override
    public void afterPropertiesSet() throws Exception {
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            if (nacosWatch != null) {
                log.info("Shutting down NacosWatch");
                try {
                    nacosWatch.stop();
                } catch (Exception e) {
                    log.info("Shutting down NacosWatch fail!", e);
                }
            }
            if (nacosAutoServiceRegistration != null) {
                log.info("Shutting down NacosAutoServiceRegistration");
                try {
                    nacosAutoServiceRegistration.stop();
                } catch (Exception e) {
                    log.info("Shutting down NacosAutoServiceRegistration fail!", e);
                }
            }
        }));
    }
} 
来源: 雨林博客(www.yl-blog.com)