zookeeper 客户端_Zookeeper 客户端异步创建连接过程分析

a8cd8d4849194b82ed100447cd0c1522.png

首先这是一个创建zk对象的代码,通过查阅资料得知 创建过程是异步的,所以我们来一探究竟。jar版本3.4.7

2c88da891f9c0ee266a3880c3df02e80.png

这段代码最后两行是重点;看下new ClientCnxn 代码

bf16bd156edfd33f1d4793681cac9b73.png

前面都是参数准备,最后两行启动了两个线程;

sendThread发送线程 eventThread 事件相关

因为最后cnxn.start();方法是启动两个线程,所以继续追踪两个Thread run方法,

先看sendThread。

4eb772cb6d26b6c40a48f38aa081e811.png

大致逻辑如下:

while (state.isAlive()) //判断连接是否存活 try {
if (!clientCnxnSocket.isConnected()) {clientCnxnSocket不是已经链接
if(!isFirstConnect){不是第一次连接
try {
Thread.sleep(r.nextInt(1000));休息
} catch (InterruptedException e) {LOG.warn("Unexpected exception", e);
}
}
// don't re-establish connection if we are closing
if (closing || !state.isAlive()) {如果连接失效 break
break;
} startConnect();重点开始 此处开始连接
clientCnxnSocket.updateLastSendAndHeard();
}//代码逻辑大致是 检测授权情况 发送一个授权事件,并记录时间 如果readTimeout或者connectTimeout则抛出异常
if (state.isConnected()) {
// determine whether we need to send an AuthFailed event.
if (zooKeeperSaslClient != null) {
boolean sendAuthEvent = false;
if (zooKeeperSaslClient.getSaslState() == ZooKeeperSaslClient.SaslState.INITIAL) {
try {
zooKeeperSaslClient.initialize(ClientCnxn.this);
} catch (SaslException e) {LOG.error("SASL authentication with Zookeeper Quorum member failed: " + e);
state = States.AUTH_FAILED;
sendAuthEvent = true;
}
}
KeeperState authState = zooKeeperSaslClient.getKeeperState();
if (authState != null) {
if (authState == KeeperState.AuthFailed) {
// An authentication error occurred during authentication with the Zookeeper Server.
state = States.AUTH_FAILED;
sendAuthEvent = true;
} else {
if (authState == KeeperState.SaslAuthenticated) {
sendAuthEvent = true;
}
}
}
if (sendAuthEvent == true) {
eventThread.queueEvent(new WatchedEvent(
Watcher.Event.EventType.None,
authState,null));
}
}
to = readTimeout - clientCnxnSocket.getIdleRecv();
} else {
to = connectTimeout - clientCnxnSocket.getIdleRecv();
}
if (to <= 0) {
String warnInfo;
warnInfo = "Client session timed out, have not heard from server in "
+ clientCnxnSocket.getIdleRecv()
+ "ms"
+ " for sessionid 0x"
+ Long.toHexString(sessionId);LOG.warn(warnInfo);
throw new SessionTimeoutException(warnInfo);
}

记录timeToNextPing 的时间
if (state.isConnected()) {
//1000(1 second) is to prevent race condition missing to send the second ping
//also make sure not to send too many pings when readTimeout is small
int timeToNextPing = readTimeout / 2 - clientCnxnSocket.getIdleSend() -
((clientCnxnSocket.getIdleSend() > 1000) ? 1000 : 0);
//send a ping request either time is due or no packet sent out within MAX_SEND_PING_INTERVAL
if (timeToNextPing <= 0 || clientCnxnSocket.getIdleSend() > MAX_SEND_PING_INTERVAL) {
sendPing();
clientCnxnSocket.updateLastSend();
} else {
if (timeToNextPing < to) {
to = timeToNextPing;
}
}
}


// If we are in read-only mode, seek for read/write server
if (state == States.CONNECTEDREADONLY) {
long now = System.currentTimeMillis();
int idlePingRwServer = (int) (now - lastPingRwServer);
if (idlePingRwServer >= pingRwTimeout) {
lastPingRwServer = now;
idlePingRwServer = 0;
pingRwTimeout =
Math.min(2*pingRwTimeout, maxPingRwTimeout);
pingRwServer();
}
to = Math.min(to, pingRwTimeout - idlePingRwServer);
} clientCnxnSocket.doTransport(to, pendingQueue, outgoingQueue, ClientCnxn.this);重点二
} catch (Throwable e) {//暂时忽略
if (closing) {
if (LOG.isDebugEnabled()) {
// closing so this is expectedLOG.debug("An exception was thrown while closing send thread for session 0x"
+ Long.toHexString(getSessionId())
+ " : " + e.getMessage());
}
break;
} else {
// this is ugly, you have a better way speak up
if (e instanceof SessionExpiredException) {LOG.info(e.getMessage() + ", closing socket connection");
} else if (e instanceof SessionTimeoutException) {LOG.info(e.getMessage() + RETRY_CONN_MSG);
} else if (e instanceof EndOfStreamException) {LOG.info(e.getMessage() + RETRY_CONN_MSG);
} else if (e instanceof RWServerFoundException) {LOG.info(e.getMessage());
} else {LOG.warn(
"Session 0x"
+ Long.toHexString(getSessionId())
+ " for server "
+ clientCnxnSocket.getRemoteSocketAddress()
+ ", unexpected error"
+ RETRY_CONN_MSG, e);
}
cleanup();
if (state.isAlive()) {
eventThread.queueEvent(new WatchedEvent(
Event.EventType.None,
Event.KeeperState.Disconnected,
null));
}
clientCnxnSocket.updateNow();
clientCnxnSocket.updateLastSendAndHeard();
}
}
}
cleanup();
clientCnxnSocket.close();
if (state.isAlive()) {
eventThread.queueEvent(new WatchedEvent(Event.EventType.None,
Event.KeeperState.Disconnected, null));
}
ZooTrace.logTraceMessage(LOG, ZooTrace.getTextTraceLevel(),
"SendThread exited loop for session: 0x"
+ Long.toHexString(getSessionId()));
}

跟踪startConnect()方法;

47bfe209051eb0bae26adeea16692f91.png

809f7e42407b5a92facf89ee8c667ee9.png

这里使用jdk nio的操作去连接:跟踪connect说明;

8d4fbd6b83fbd48f1d647b4f036342f9.png

大致意思如下:

如果此通道处于非阻塞模式,则对此通道的调用,方法启动非阻塞连接操作。 如果连接立即建立,就像本地连接一样,然后此方法返回<tt> true </ tt>。 否则,此方法返回 <tt> false </ tt>,稍后必须完成连接操作调用{@link #finishConnect finishConnect}方法。

如果此通道处于阻止模式,则此通道的调用方法将阻塞,直到建立连接或I / O错误发生。

既然是nio非阻塞调用,那么什么时候调用finishConnect方法呢?答案就是重点二了;

跟踪clientCnxnSocket.doTransport(to, pendingQueue, outgoingQueue, ClientCnxn.this);

342cf114f84841791124f2736a0b0b15.png

看下方法说明

e8bcfc8f0b4158c577fff360c501ab95.png

此时是判断socket是否真的连接;

之后执行sendThread.primeConnection();

最后执行outgoingQueue队列;

所以最前面的代码需要这样写;

aa4b4f81d71e4c109410d35505ffa68d.png

CountDownLatch 会防止因为网络问题连接没有生成 而去操作的异常。这也是zk创建异步的原因

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值