诡异的问题:Dubbo注册zookeeper协议时,竟然出现了这种异常提示

简介: 遇到一个很诡异的问题,我在启动多个配置相同zookeeper的Dubbo项目时,其他项目都是正常启动,唯独有一个项目在启动过程中,Dubbo注册zookeeper协议时,竟然出现了这样的异常提示——

遇到一个很诡异的问题,我在启动多个配置相同zookeeper的Dubbo项目时,其他项目都是正常启动,唯独有一个项目在启动过程中,Dubbo注册zookeeper协议时,竟然出现了这样的异常提示——

Caused by: java.lang.IllegalStateException: zookeeper not connected
  at org.apache.dubbo.remoting.zookeeper.curator.CuratorZookeeperClient.<init>(CuratorZookeeperClient.java:80)
  ... 79 common frames omitted

我愣了一下,原以为是zookeeper集群挂了,然后检查了一下,都正常啊,奇怪的是,其他系统也是正常连接,为啥会有一台出现了这样的异常呢?

看了一下异常提示,当我深入研究了一下出错的地方时,才恍然明白出现这个异常究竟是为什么了。

可谓是,在源码面前,一切都是裸泳。

先来看异常提示出现的类方法CuratorZookeeperClient,这个方法的作用是建立zookeeper客户端的连接,类似http通信一般,在建立通信前,需要先建立三次握手连接,同理,在zookeeper客户端创建各类节点前,同样需要先建立客户端连接到服务器上——

public CuratorZookeeperClient(URL url) {
        super(url);
        try {
            int timeout = url.getParameter(TIMEOUT_KEY, DEFAULT_CONNECTION_TIMEOUT_MS);
            int sessionExpireMs = url.getParameter(ZK_SESSION_EXPIRE_KEY, DEFAULT_SESSION_TIMEOUT_MS);
            CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder()
                    .connectString(url.getBackupAddress())
                    .retryPolicy(new RetryNTimes(1, 1000))
                    .connectionTimeoutMs(timeout)
                    .sessionTimeoutMs(sessionExpireMs);
            String authority = url.getAuthority();
            if (authority != null && authority.length() > 0) {
                builder = builder.authorization("digest", authority.getBytes());
            }
            client = builder.build();
            client.getConnectionStateListenable().addListener(new CuratorConnectionStateListener(url));
            client.start();
            boolean connected = client.blockUntilConnected(timeout, TimeUnit.MILLISECONDS);
            if (!connected) {
                throw new IllegalStateException("zookeeper not connected");
            }
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

根据CuratorZookeeperClient方法可知,出现zookeeper not connected异常提示是发生在这一段代码当中——

if (!connected) {
    throw new IllegalStateException("zookeeper not connected");
}

connected表示连接状态,当它的值为false时,便会执行这段代码,那么,究竟是什么情况会导致它的值为false呢?

接下来,让我们打一个断点,一步一步解析这段代码。

首先,用作测试的dubbo和zookeeper配置如下——

dubbo:
  application:
    name: testervice
  registry:
    address: zookeeper://120.77.217.245
#    timeout: 20000
  protocol:
    name: dubbo
    port: 20880

解析来,开始debug,打断点,CuratorZookeeperClient方法参数url主要包含以下信息——


第一步、从url中获取超时时间timeout参数——

int timeout = url.getParameter(TIMEOUT_KEY, DEFAULT_CONNECTION_TIMEOUT_MS);

这里的大概逻辑是,如果yaml配置registry注册zookeeper部分参数当中含有 timeout话,那么就返回配置当中定义的超时时间,如果yaml没有进行配置,那么,就用默认的超时时间,默认即常量DEFAULT_CONNECTION_TIMEOUT_MS,值是5 * 1000,也就是5秒,这个参数其实就是本篇文章的核心。

若自定义形式配置该参数,形式如下timeout: 20000——

dubbo:
  application:
    name: testervice
  registry:
    address: zookeeper://120.77.217.245
    timeout: 20000

第二步、获取客户端过期时间——

int sessionExpireMs = url.getParameter(ZK_SESSION_EXPIRE_KEY, DEFAULT_SESSION_TIMEOUT_MS);

同理,无自定义配置话,则使用默认值DEFAULT_SESSION_TIMEOUT_MS = 60 * 1000,即6分钟;

第三步、创建一个设置过期时间为6分钟,连接超时为5秒,重试策略为每秒重试一次,连接服务端为url.getBackupAddress()(注:我这里得到的是120.77.217.245:9090,即配置的zookeeper连接url)的CuratorFramework客户端实例——

CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder()
          .connectString(url.getBackupAddress())
          .retryPolicy(new RetryNTimes(1, 1000))
          .connectionTimeoutMs(timeout)
          .sessionTimeoutMs(sessionExpireMs);
client = builder.build();

第四步、添加连接状态的监控,可以监控操作节点与连接情况——

client.getConnectionStateListenable().addListener(new CuratorConnectionStateListener(url));

第五步、开启客户端——

client.start();

最后一步,监控客户端连接情况,若能连接成功,则证明创建客户端成功,反之,失败。可见,若出现zookeeper not connected,问题就在于客户端连接过程是失败的,至于为何失败,原理就在client.blockUntilConnected(timeout, TimeUnit.MILLISECONDS)代码里。

boolean connected = client.blockUntilConnected(timeout, TimeUnit.MILLISECONDS);
if (!connected) {
       throw new IllegalStateException("zookeeper not connected");
}

进入到 client.blockUntilConnected(timeout, TimeUnit.MILLISECONDS)源码里,这里的maxWaitTime即前边的timeout,默认值是5秒,大概分析一下下边代码——

public synchronized boolean blockUntilConnected(int maxWaitTime, TimeUnit units) throws InterruptedException
{
    //获取当前时间
    long startTime = System.currentTimeMillis();
    //这里是true
    boolean hasMaxWait = (units != null);
    //maxWaitTimeMs等于5000毫秒,即5秒
    long maxWaitTimeMs = hasMaxWait ? TimeUnit.MILLISECONDS.convert(maxWaitTime, units) : 0;
    while ( !isConnected() )
    {
        //hasMaxWait为true
        if ( hasMaxWait )
        {   
            //倒数5秒
            long waitTime = maxWaitTimeMs - (System.currentTimeMillis() - startTime);
            //执行到这里,已经过去5秒话,就执行以下方法,返回isConnected()值
            if ( waitTime <= 0 )
            {
                return isConnected();
            }
           //还没到5秒话,假如执行到这里还有3秒,那么就会执行Object.wait(long timeout)方法,即该线程阻塞3秒后再自动唤醒,接着继续执行
            wait(waitTime);
        }
        else
        {
            wait();
        }
    }
    return isConnected();
}

该方法的核心会等待maxWaitTime时间,时间一到,就会返回isConnected()值,这里其实很好理解,就是客户端发起连接后,这里用一个while循环来等待指定的超时时间,默认是5秒,若5秒过了,就返回isConnected()值,而这里的isConnected()就是验证是否连接成功了,

那么,这里就剩最后一个答案了,isConnected()是什么?

public synchronized boolean isConnected(){
     return (currentConnectionState != null) && currentConnectionState.isConnected();
}

这里应该是判断客户端连接状态,即在client.start()方法里,会有一个状态,若创建连接成功,那么currentConnectionState.isConnected()就能得到true值,这里更像是一个观察模式,观察指定的连接超时时间内,是否连接成功。

根据debug,发现未连接成功时,值是null,得到的即为false,当我们把默认为5秒的连接超时设置为timeout: 20000,等待连接过程,发现连接成功了,返回currentConnectionState的值为RECONNECTED。

可见,之前出现zookeeper not connected异常问题,就是连接超时设置太短了!


currentConnectionState.isConnected()得到的是一个枚举值,RECONNECTED返回的是true——

CONNECTED {
        public boolean isConnected() {
            return true;
        }
    },
    SUSPENDED {
        public boolean isConnected() {
            return false;
        }
    },
    RECONNECTED {
        public boolean isConnected() {
            return true;
        }
    },
    LOST {
        public boolean isConnected() {
            return false;
        }
    },
    READ_ONLY {
        public boolean isConnected() {
            return true;
        }
    };

当返回true话,那么!connected就为false,就不会执行以下异常提示了——

if (!connected) {
       throw new IllegalStateException("zookeeper not connected");
}

根据上边分析,可见启动Dubbo项目注册Zookeeper时提示zookeeper not connected异常,是因为没有在配置里设置连接超时,而是使用了默认的5秒,导致5秒内没有成功连接,就出现连接异常而无法成功连接,当调长时间后,就正常连接成功了,同时也说明了,这次本地连接zookeeper集群的时间超过了五秒。

本文就是愿天堂没有BUG给大家分享的内容,大家有收获的话可以分享下,想学习更多的话可以到微信公众号里找我,我等你哦。

相关文章
|
2月前
|
消息中间件 分布式计算 资源调度
《聊聊分布式》ZooKeeper与ZAB协议:分布式协调的核心引擎
ZooKeeper是一个开源的分布式协调服务,基于ZAB协议实现数据一致性,提供分布式锁、配置管理、领导者选举等核心功能,具有高可用、强一致和简单易用的特点,广泛应用于Kafka、Hadoop等大型分布式系统中。
|
安全 应用服务中间件 API
微服务分布式系统架构之zookeeper与dubbo-2
微服务分布式系统架构之zookeeper与dubbo-2
|
Dubbo 安全 应用服务中间件
Apache Dubbo 正式发布 HTTP/3 版本 RPC 协议,弱网效率提升 6 倍
在 Apache Dubbo 3.3.0 版本之后,官方推出了全新升级的 Triple X 协议,全面支持 HTTP/1、HTTP/2 和 HTTP/3 协议。本文将围绕 Triple 协议对 HTTP/3 的支持进行详细阐述,包括其设计目标、实际应用案例、性能测试结果以及源码架构分析等内容。
776 112
|
存储 负载均衡 监控
dubbo学习一:zookeeper与dubbo的关系,下载安装启动zookeeper(解决启动中报错)
这篇文章是关于Apache Dubbo框架与Zookeeper的关系,以及如何下载、安装和启动Zookeeper的教程,包括解决启动过程中可能遇到的报错问题。
583 3
dubbo学习一:zookeeper与dubbo的关系,下载安装启动zookeeper(解决启动中报错)
|
Dubbo Java 应用服务中间件
深入探讨了“dubbo+nacos+springboot3的native打包成功后运行出现异常”的原因及解决方案
本文深入探讨了“dubbo+nacos+springboot3的native打包成功后运行出现异常”的原因及解决方案。通过检查GraalVM版本兼容性、配置反射列表、使用代理类、检查配置文件、禁用不支持的功能、查看日志文件、使用GraalVM诊断工具和调整GraalVM配置等步骤,帮助开发者快速定位并解决问题,确保服务的正常运行。
496 1
|
Dubbo 应用服务中间件 Apache
Dubbo 应用切换 ZooKeeper 注册中心实例,流量无损迁移
如果 Dubbo 应用使用 ZooKeeper 作为注册中心,现在需要切换到新的 ZooKeeper 实例,如何做到流量无损?
193 4
|
监控 Dubbo Java
dubbo学习三:springboot整合dubbo+zookeeper,并使用dubbo管理界面监控服务是否注册到zookeeper上。
这篇文章详细介绍了如何将Spring Boot与Dubbo和Zookeeper整合,并通过Dubbo管理界面监控服务注册情况。
1006 0
dubbo学习三:springboot整合dubbo+zookeeper,并使用dubbo管理界面监控服务是否注册到zookeeper上。
|
分布式计算 负载均衡 算法
Hadoop-31 ZooKeeper 内部原理 简述Leader选举 ZAB协议 一致性
Hadoop-31 ZooKeeper 内部原理 简述Leader选举 ZAB协议 一致性
162 1
|
负载均衡 Java 应用服务中间件
微服务分布式系统架构之zookeeper与dubbor-1
微服务分布式系统架构之zookeeper与dubbor-1
|
存储 负载均衡 Dubbo
分布式-Zookeeper(一)
分布式-Zookeeper(一)

相关产品

  • 微服务引擎