zookeeper Java客户端连接问题
具体参考文章 ConnectionLoss异常解决
zookeeper客户端连接特点:zookeeper建立连接时采用异步操作,连接操作后并不能保证zookeeper已连接成功,如果在zookeeper连接成功前访问相应节点,会出现ConnectionLoss错误。
解决思路:在新建zookeeper连接后,保证连接成功后再访问zookeeper。
具体代码:
public static void waitUntilConnected(ZooKeeper testZooKeeper, CountDownLatch testLatch) {
if (testZooKeeper.getState() == ZooKeeper.States.CONNECTING) {
try {
testLatch.await(); //此函数会一直阻塞当前进程,直到计数器的值为0为止
} catch (Exception e) {
e.printStackTrace(printWriter);
System.out.println("Latch exception");
}
}
}
static class ConnectedWatcher implements org.apache.zookeeper.Watcher {
private CountDownLatch connectedLatch;
ConnectedWatcher(CountDownLatch connectedLatch) {
this.connectedLatch = connectedLatch; /* CountDownLatch实例初始化时设为1即可 */
}
@Override
public void process(WatchedEvent event) {
if (event.getState() == Event.KeeperState.SyncConnected) {
connectedLatch.countDown(); /* ZK连接成功时,计数器由1减为0 */
}
}
}
/**
* 初始化zookeeper连接
*/
public boolean newZookeeperLink() {
try {
if (zooKeeper != null && zooKeeper.getState() == ZooKeeper.States.CONNECTED) {
return true;
} else {
destroy();
CountDownLatch sampleLatch = new CountDownLatch(1);
Watcher sampleWatcher = new ConnectedWatcher(sampleLatch);
zooKeeper = new ZooKeeper(zookeeperIPUtil.getIP() + ":" + zookeeperIPUtil.getPort(), 20000, sampleWatcher);
waitUntilConnected(zooKeeper, sampleLatch); /*只有当ZK链接成功(状态为SyncConnected)时,此函数调用才结束*/
logger.info("创建新的zookeeper连接");
return true;
}
} catch (Exception e) {
logger.info("zookeeper初始化连接失败");
e.printStackTrace(printWriter);
logger.error(printWriter.toString());
return false;
}
}
其中使用到了两个类:
- java.util.concurrent.CountDownLatch(同步辅助类,类似倒数计数,直到计数器为0时才能对资源“解锁”,未解锁前等待该资源的进程只能被阻塞)
- org.apache.zookeeper.Watcher
注意!
不要轻易创建新的zookeeper连接,连接数过多时zookeeper服务端会阻止新的连接生成。
close()方法虽然会关闭zookeeper连接,但是服务端端口会由ESTABLISHED状态变为TIME_WAIT,并且需要一段时间才会完全将此连接删除,当TIME_WAIT状态的连接过多时也会导致服务端阻止新的连接生成的问题。