文/朱季谦

遇到一个很诡异的问题,我在启动多个配置相同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集群的时间超过了五秒。

启动Dubbo项目注册Zookeeper时提示zookeeper not connected异常原理解析的更多相关文章

  1. 使用tomcat启动dubbo项目

    首先,黑体标出 官方不推荐使用web容器进行dubbo的启动 但是,有些时候,我们不采用他们的建议. 背景: 之前用的dubbo项目,是由main函数启动的,每次发布项目,需要启动两项: 1. tom ...

  2. 在Eclipse中运行Web项目Jsp网页时提示端口被占用的解决办法:Several ports (8005, 8888, 8009) required by Tomcat v9.0 Server at localhost are already in use.

    问题: 在Eclipse中运行Web项目Jsp网页启动Tomcat时提示端口被占用: Several ports (8005, 8080, 8009) required by Tomcat v9.0 ...

  3. maven项目编译运行时提示jdk版本过低问题解决方法

    明明使用的是1.8jdk,但是运行项目时提示使用的是java版本是1.5,版本过低. 修改pom.xml,添加如下: <build> <plugins> <plugin& ...

  4. 部署项目到weblogic时提示文件被锁,导致报错

    部署项目到weblogic中出现一个“黄叹号!”.报错如下: (1) Deployment is out of date due to changes in the underlying projec ...

  5. nginx进行项目域名配置时提示Job for nginx.service failed

    ps aux | grep nginx /bin/systemctl stop nginx.service /bin/systemctl start nginx.service /bin/system ...

  6. tomcat启动dubbo报IO异常

    严重: Servlet [springDispatcher] in web application [/dubbo-service] threw load() exception java.io.IO ...

  7. Dubbo配置注册中心设置application的name使用驼峰命名法存在的隐藏项目启动异常问题

    原创/朱季谦 首先,先提一个建议,在SpringBoot+Dubbo项目中,Dubbo配置注册中心设置的application命名name的值,最好使用xxx-xxx-xxx这样格式的,避免随便使用驼 ...

  8. 【Dubbo】Zookeeper+Dubbo项目demo搭建

    一.Dubbo的注解配置 在Dubbo 2.6.3及以上版本提供支持. 1.@Service(全路径@org.apache.dubbo.config.annotation.Service) 配置服务提 ...

  9. Dubbo原理解析-注册中心之Zookeeper协议注册中心

    下面我们来看下开源dubbo推荐的业界成熟的zookeeper做为注册中心, zookeeper是hadoop的一个子项目是分布式系统的可靠协调者,他提供了配置维护,名字服务,分布式同步等服务.对于z ...

随机推荐

  1. OSS对象存储的文件上传、解冻、下载与查看

    上传文件 cp命令用于上传.下载.拷贝文件. # 语法 ./ossutil cp [-r] file_url cloud_url # 例如 ossutil64 cp -r /remote/closed ...

  2. Tomcat部署与优化

    目录: 一.Tomcat概述 二.Tomcat 服务部署 三.Tomcat 虚拟主机配置 四.Tomcat 优化 一.Tomcat概述 Tomcat是Java语言开发的,Tomcat服务器是-个免费的 ...

  3. 网络游戏逆向分析-6-使用背包物品call

    网络游戏逆向分析-6-使用背包物品call 首先,大家在处理网络游戏的时候,一定得利用好发包函数,因为他就是整个网络游戏的关键. 处理办法: 这里还是直接给发包打断点来处理. 就像我们之前处理喊话函数 ...

  4. json包中的Marshal&Unmarshal 文档译本

    Marshal func Marshal(v interface{})([]byte, error) Marshal returns the JSON encoding of v. Marshal返回 ...

  5. Docker安装Nginx(含:Windows启动、重启、停止)

    Docker安装Nginx #docker pull nginx:latest (第一次启动Docker-Nginx) #docker run --detach \ --publish 80:80 \ ...

  6. Jupyter Notebook 修改字体和大小

    1 打开这个目录 D:\SoftWare\Anaconda\Lib\site-packages\notebook\static\custom 点开这个 也就是你安装的 python下面的 jupyte ...

  7. mybatis一对一联表查询的两种常见方式

    1.一条语句执行查询(代码如下图)  注释:class表(c别名),teacher表(t别名)teacher_id为class表的字段t_id为teacher表的字段,因为两者有主键关联的原因,c_i ...

  8. 为Python安装Redis库

    为Python安装Redis库,登陆https://github.com/andymccurdy/redis-py 后点击Download ZIP下载安装包. 解压并安装: git clone htt ...

  9. shell加密为二进制可执行文件

    1.下载shc工具 http://www.datsi.fi.upm.es/~frosal/sources/shc-3.8.9.tgz或者到http://www.datsi.fi.upm.es/~fro ...

  10. 一文彻底掌握Apache Hudi异步Clustering部署

    1. 摘要 在之前的一篇博客中,我们介绍了Clustering(聚簇)的表服务来重新组织数据来提供更好的查询性能,而不用降低摄取速度,并且我们已经知道如何部署同步Clustering,本篇博客中,我们 ...