Zookeeper客户端断开链接时报错

遇到的问题
客户端程序退出时报如下错误

2023-02-26 09:18:08.342 [main-EventThread] ERROR o.apache.curator.framework.recipes.cache.NodeCache - 
java.lang.IllegalStateException: Expected state [STARTED] was [STOPPED]
    at org.apache.curator.shaded.com.google.common.base.Preconditions.checkState(Preconditions.java:823)
    at org.apache.curator.framework.imps.CuratorFrameworkImpl.checkState(CuratorFrameworkImpl.java:423)
    at org.apache.curator.framework.imps.CuratorFrameworkImpl.checkExists(CuratorFrameworkImpl.java:450)
    at org.apache.curator.framework.recipes.cache.NodeCache.reset(NodeCache.java:261)
    at org.apache.curator.framework.recipes.cache.NodeCache.access$100(NodeCache.java:60)
    at org.apache.curator.framework.recipes.cache.NodeCache$2.process(NodeCache.java:104)
    at org.apache.curator.framework.imps.NamespaceWatcher.process(NamespaceWatcher.java:77)
    at org.apache.zookeeper.ClientCnxn$EventThread.processEvent(ClientCnxn.java:580)
    at org.apache.zookeeper.ClientCnxn$EventThread.run(ClientCnxn.java:555)

业务简单描述

  • SpringBoot + Dubbo3写的一个服务,Zookeeper为注册中心;
  • Client端是定时任务,通过crontab进行调度;
  • 目前Server端没有报错,Client端在程序退出时报以上错误;
  • Client目前是启动执行完成业务后会退出,不会await,此时就会报上述错误;
  • Client如果启动后执行完成业务后不退出,一直await,则不会报上述错误。

环境

名称版本
JDK17
SpringBoot2.7.9
Dubbo3.1.7

Client端相关代码

@Slf4j
@EnableDubbo
@SpringBootApplication
public class Application implements CommandLineRunner {
    @Value("${spring.profiles.active}")
    private String springProfile;
    @DubboReference(
            version = "${services.version}"
            , url = "${services.url}")
    private TestServices services;

    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = SpringApplication.run(Application.class, args);
        ApplicationModel.defaultModel().destroy();
        //System.exit(SpringApplication.exit(ctx, () -> 0));
    }

    @Override
    public void run(String... strings) {
        log.info(">>>>>>{}<<<<<<", springProfile);
        log.info("Result:{}", services.test());
    }
}

Client端pom中依赖

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.9</version>
        <relativePath/>
    </parent>
    <properties>
        <dubbo.version>3.1.7</dubbo.version>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
......
    <dependencies>
        <!-- .............................................. spring boot ...-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- .............................................. dubbo ...-->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
            <version>${dubbo.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>${dubbo.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
            <version>${dubbo.version}</version>
            <type>pom</type>
            <exclusions>
                <exclusion>
                    <artifactId>slf4j-reload4j</artifactId>
                    <groupId>org.slf4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

yml配置

server:
  shutdown: graceful

dubbo:
  application:
    name: test-consumer
  registry:
    id: test-consumer
    address: zookeeper://192.168.1.100:2181
  config-center:
    address: zookeeper://192.168.1.100:2181
  metadata-report:
    address: zookeeper://192.168.1.100:2181
  consumer:
    timeout: 3000
    check: false
    retries: 0

services:
  version: 1.0.0
  url:

望赐教!

该回答引用GPTᴼᴾᴱᴺᴬᴵ
根据您提供的信息,看起来问题出现在您的 CuratorFramework 调用。具体来说,错误信息中的 java.lang.IllegalStateException 表示您的代码正在尝试使用已停止的 CuratorFramework 对象进行操作,而这显然是不允许的。

可能的原因之一是在客户端程序退出时没有正确关闭 CuratorFramework 对象,导致该对象在程序退出时仍处于已启动状态,但此时已无法继续使用该对象进行操作。

为解决这个问题,您可以尝试在客户端程序退出时显式地关闭 CuratorFramework 对象,例如:

CuratorFramework client = ...; // 获取 CuratorFramework 对象
client.close(); // 关闭对象


另外,您的代码中似乎缺少了变量 key 的定义,这可能会导致编译错误或运行时异常。请确保 key 变量已经被正确定义和初始化。

该回答引用ChatGPT
根据报错信息可以看出,NodeCache对象的状态不符合期望,期望状态是 STARTED,但实际状态是 STOPPED,所以触发了 IllegalStateException 异常。

可能原因是,当你的程序退出时,NodeCache 对象并没有被正确关闭,导致其状态仍然是 STOPPED,而不是 CLOSED。这可能会导致 Zookeeper 客户端出现问题。

你可以尝试在程序退出前手动关闭 NodeCache 对象,如下所示:


// 在程序退出前手动关闭 NodeCache 对象
@DubboReference(version = "${services.version}", url = "${services.url}")
private TestServices services;
private NodeCache nodeCache;

@Override
public void run(String... strings) {
    // 创建 NodeCache 对象并启动
    nodeCache = new NodeCache(client, path, false);
    nodeCache.start();

    log.info(">>>>>>{}<<<<<<", springProfile);
    log.info("Result:{}", key, services.test());
}

@PreDestroy
public void onDestroy() throws Exception {
    // 程序退出时手动关闭 NodeCache 对象
    if (nodeCache != null) {
        nodeCache.close();
    }
}

你也可以尝试将 NodeCache 对象注入到 Spring 容器中,然后使用 @PreDestroy 注解在程序退出前手动关闭 NodeCache 对象。

参考GPT和自己的思路,根据报错信息,问题可能出在 NodeCache 上,该类是 Curator Framework 提供的缓存节点数据的类,它能够监听指定节点的变化并缓存节点的数据,当节点发生变化时会触发监听事件。在该异常中,异常信息显示 NodeCache 的状态是 STOPPED,而程序期望的状态应该是 STARTED,这可能是因为程序在退出时没有正确地关闭 NodeCache 导致的。

解决方法如下:

1.在程序退出时,显示地调用 NodeCache 的 close 方法来关闭缓存,释放资源,例如:

@PreDestroy
public void destroy() throws IOException {
nodeCache.close();
}

2.如果不希望在程序退出时关闭 NodeCache,可以将 NodeCache 对象设置为 Daemon 线程,这样程序退出时不会阻止 JVM 关闭,例如:

nodeCache.setExecutor(Executors.newSingleThreadExecutor(r -> {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
}));

这样就可以在程序退出时不阻止 JVM 关闭,从而避免了可能因为 NodeCache 未正确关闭而引起的错误。

你这个错误表明 NodeCache 实例在 Zookeeper 的连接已经关闭的情况下被使用,因此出现了异常状态,你需要确保在退出程序之前没有关闭 CuratorFramework 实例,可以通过将 dubbo.shutdown.wait 属性设置为 5000 或更长时间来解决这个问题

springboot集成dubbo踩坑文档
借鉴下,非常详细
https://blog.csdn.net/bug01111/article/details/124178275

为啥客户端要退出?我没理解

不知道你这个问题是否已经解决, 如果还没有解决的话:

如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^
  • @programmer_ada 我自行找到了以下一篇博文:
    dubbo 使用 curator 作为 zk 客户端优雅停机存在的问题 --- 作者:林一
  • 我也试着跟踪了一下代码,发现的确向文章中说的情况一样;
  • 虽说文章中遇到的问题和我的不尽相同,且开发包的版本也都不一样,但我认为情况类似;
  • 我再等等看吧,看看会不会有大牛解决我的问题;
  • @嗝屁小孩纸 多谢您的回复了,给了我另一个思路来考虑这个问题,否则我也找不到上面的文章;
  • 顺便说一下,百度真烂,我是用bing搜到的,bing啥时候接入ChatGPT啊?!
  • @pzzhao 如果问题不能彻底解决,我也会考虑任务驻留后台,但前提要再增加一台服务器;

Zookeeper客户端断开链接时,可能会出现ConnectionLossException异常,该异常表明客户端连接断开,无法更新或访问数据。一般来说,该异常是由网络连接问题或服务器故障导致的。可以尝试检查网络连接,或重启Zookeeper服务器,以解决该问题。

https://www.baidu.com/link?url=8X3dU_FDCGQha_6Z46_UAorrlgkp8ePgcz2M7WM1db6emCFDkWW1JYKKMlWkUMZZvx_8_v-59bupJzKRDDVxETXksRrJ4BXowKZIJRAk0Wu&wd=&eqid=eddc1219000ed7bc0000000263fddab6