Spring Boot应用运行时抛出Curator异常崩溃,求原因分析
Spring Boot应用崩溃异常分析与修复
异常链梳理
1. 根源异常:ZooKeeper地址解析失败触发NPE
前序日志中的NullPointerException是问题起点:
2023-02-16 16:01:11,320 [main-EventThread] [17] org.apache.curator.framework.imps.CuratorFrameworkImpl:699 - Background exception was not retry-able or retry gave up java.lang.NullPointerException: Cannot invoke "java.net.InetAddress.getHostAddress()" because the return value of "java.net.InetSocketAddress.getAddress()" is null at org.apache.curator.utils.Compatibility.getHostAddress(Compatibility.java:116) at org.apache.curator.framework.imps.EnsembleTracker.configToConnectionString(EnsembleTracker.java:185) at org.apache.curator.framework.imps.EnsembleTracker.processConfigData(EnsembleTracker.java:206) at org.apache.curator.framework.imps.EnsembleTracker.access$300(EnsembleTracker.java:50) at org.apache.curator.framework.imps.EnsembleTracker$2.processResult(EnsembleTracker.java:150) at org.apache.curator.framework.imps.CuratorFrameworkImpl.sendToBackgroundCallback(CuratorFrameworkImpl.java:892) at org.apache.curator.framework.imps.CuratorFrameworkImpl.processBackgroundOperation(CuratorFrameworkImpl.java:649) at org.apache.curator.framework.imps.WatcherRemovalFacade.processBackgroundOperation(WatcherRemovalFacade.java:152) at org.apache.curator.framework.imps.GetConfigBuilderImpl$2.processResult(GetConfigBuilderImpl.java:222) at org.apache.zookeeper.ClientCnxn$EventThread.processEvent(ClientCnxn.java:634) at org.apache.zookeeper.ClientCnxn$EventThread.run(ClientCnxn.java:553)
原因:Curator解析ZooKeeper集群配置时,某个节点的InetSocketAddress.getAddress()返回null,说明配置的ZooKeeper地址无效——可能是主机名无法DNS解析、地址格式错误(如只写端口未写主机),或者集群配置返回了空地址信息。重试机制耗尽后,触发了应用关闭流程。
2. 崩溃触发点:关闭流程中执行配置刷新
后续的IllegalStateException是导致应用直接崩溃的直接原因:
2023-02-16 17:03:15,576 [Curator-TreeCache-1] [45] org.apache.curator.framework.recipes.cache.TreeCache:827 - java.lang.IllegalStateException: Shutdown in progress at org.springframework.util.Assert.state(Assert.java:76) at org.springframework.boot.SpringApplicationShutdownHook.assertNotInProgress(SpringApplicationShutdownHook.java:166) at org.springframework.boot.SpringApplicationShutdownHook.registerApplicationContext(SpringApplicationShutdownHook.java:75) at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:413) at org.springframework.boot.SpringApplication.run(SpringApplication.java:303) at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:164) at org.springframework.cloud.context.refresh.LegacyContextRefresher.addConfigFilesToEnvironment(LegacyContextRefresher.java:78) at org.springframework.cloud.context.refresh.LegacyContextRefresher.updateEnvironment(LegacyContextRefresher.java:56) at org.springframework.cloud.context.refresh.ContextRefresher.refreshEnvironment(ContextRefresher.java:101) at org.springframework.cloud.context.ContextRefresher.refresh(ContextRefresher.java:94) at org.springframework.cloud.endpoint.event.RefreshEventListener.handle(RefreshEventListener.java:72) at org.springframework.cloud.endpoint.event.RefreshEventListener.onApplicationEvent(RefreshEventListener.java:61) at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176) at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169) at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143) at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:421) at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:378) at org.springframework.cloud.zookeeper.config.ConfigWatcher.childEvent(ConfigWatcher.java:118) at org.apache.curator.framework.recipes.cache.TreeCache.lambda$callListeners$0(TreeCache.java:810) at org.apache.curator.framework.listen.MappingListenerManager.lambda$forEach$0(MappingListenerManager.java:92) at org.apache.curator.framework.listen.MappingListenerManager.forEach(MappingListenerManager.java:89) at org.apache.curator.framework.listen.StandardListenerManager.forEach(StandardListenerManager.java:89) at org.apache.curator.framework.recipes.cache.TreeCache.callListeners(TreeCache.java:806) at org.apache.curator.framework.recipes.cache.TreeCache.access$1800(TreeCache.java:78) at org.apache.curator.framework.recipes.cache.TreeCache$2.run(TreeCache.java:908) at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) at java.base/java.lang.Thread.run(Thread.java:833)
原因:应用已进入关闭流程,但ZooKeeper的TreeCache监听器仍捕获到配置节点变化,触发了Spring Cloud的配置刷新事件。刷新流程尝试注册新的ApplicationContext,而Spring的关闭钩子检测到关闭正在进行,抛出非法状态异常,直接终止应用。
修复方案
- 修正ZooKeeper连接配置:
- 检查
spring.cloud.zookeeper.connect-string,确保每个节点格式为IP/主机名:端口,例如10.0.0.5:2181,10.0.0.6:2181 - 验证配置的主机名可正常解析(使用
ping或nslookup测试),避免DNS解析失败导致地址为空
- 检查
- 优化Curator重试与关闭逻辑:
- 调整Curator的重试策略(如增加重试次数、延长间隔),避免因短暂连接异常触发应用关闭
- 在应用关闭阶段禁用ZooKeeper配置监听器,阻止刷新事件触发;或配置Spring Cloud刷新机制在关闭时跳过执行
- 版本升级修复已知bug:
- 检查Spring Cloud Zookeeper与Curator版本兼容性,部分旧版本存在集群地址解析的bug,升级至稳定匹配版本(例如Spring Cloud 2022.0.x搭配Curator 5.2.1)
内容的提问来源于stack exchange,提问作者Chetan Krishna
相关产品推荐
相关产品推荐

