郑州品牌网站建设费用,搜索引擎广告形式有,企业市场营销,制作单页网站要网址原创/朱季谦
遇到一个很诡异的问题#xff0c;我在启动多个配置相同zookeeper的Dubbo项目时#xff0c;其他项目都是正常启动#xff0c;唯独有一个项目在启动过程中#xff0c;Dubbo注册zookeeper协议时#xff0c;竟然出现了这样的异常提示——
Caused by: java.lang.…
原创/朱季谦
遇到一个很诡异的问题我在启动多个配置相同zookeeper的Dubbo项目时其他项目都是正常启动唯独有一个项目在启动过程中Dubbo注册zookeeper协议时竟然出现了这样的异常提示——
Caused by: java.lang.IllegalStateException: zookeeper not connectedat 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: testerviceregistry:address: zookeeper://120.77.217.245
# timeout: 20000protocol:name: dubboport: 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: testerviceregistry:address: zookeeper://120.77.217.245timeout: 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();//这里是trueboolean hasMaxWait (units ! null);//maxWaitTimeMs等于5000毫秒即5秒long maxWaitTimeMs hasMaxWait ? TimeUnit.MILLISECONDS.convert(maxWaitTime, units) : 0;while ( !isConnected() ){//hasMaxWait为trueif ( 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集群的时间超过了五秒。