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