2021SC@SDUSC
前言
接下来将进入源码世界来一步一步分析客户端与服务端之间是如何通过ClientCnxn/ServerCnxn来建立起网络通信的。而这次内容将分为三章来讲述,在本章中将介绍客户端是如何将请求发送到服务端的,后两章将分别介绍服务端是如何响应客户端请求的以及客户端收到服务端的响应之后是如何操作的。
ZooKeeper中网络通信执行流程
① 在ZooKeeper的构造函数中,创建了客户端与服务端之间的ClientCnxn交互连接。从而能使客户端发出的请求通过该交互连接传输给服务端,其中createConnection方法返回的是ClientCnxn。
// 创建客户端连接,并初始化SendThread和EventThread
cnxn = createConnection(
connectStringParser.getChrootPath(),
hostProvider,
sessionTimeout,
this,
watchManager,
getClientCnxnSocket(),
canBeReadOnly);
// 启动SendThread和EventThread
cnxn.start();
创建客户端连接的具体代码如下:
protected ClientCnxn createConnection(
// 客户端路径
String chrootPath,
// 服务端
HostProvider hostProvider,
// 会话超时
int sessionTimeout,
ZooKeeper zooKeeper,
// 客户端监听器
ClientWatchManager watcher,
// 客户端连接Socket
ClientCnxnSocket clientCnxnSocket,
boolean canBeReadOnly) throws IOException {
return new ClientCnxn(
chrootPath,
hostProvider,
sessionTimeout,
this,
watchManager,
clientCnxnSocket,
canBeReadOnly);
}
② sendThread是ClientCnxn的内部类,也是ZooKeeper中的一个线程,核心是run()方法。
(1)在run()方法中,如果客户端连接没有开始创建,那么会调用sendThread()中的startConnect()方法进行异步连接。
public void run() {
clientCnxnSocket.introduce(this, sessionId, outgoingQueue);
clientCnxnSocket.updateNow();
clientCnxnSocket.updateLastSendAndHeard();
int to;
long lastPingRwServer = Time.currentElapsedTime();
final int MAX_SEND_PING_INTERVAL = 10000; //10 seconds
InetSocketAddress serverAddress = null;
while (state.isAlive()) {
try {
// 如果客户端连接没有连接起来
if (!clientCnxnSocket.isConnected()) {
// don't re-establish connection if we are closing
if (closing) {
break;
}
if (rwServerAddress != null) {
serverAddress = rwServerAddress;
rwServerAddress = null;
} else {
serverAddress = hostProvider.next(1000);
}
onConnecting(serverAddress);
//异步连接
startConnect(serverAddress);
// Update now to start the connection timer right after we make a connection attempt
clientCnxnSocket.updateNow();
clientCnxnSocket.updateLastSendAndHeard();
}
// 如果客户端连接已经连接上服务端
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);
changeZkState(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.
changeZkState(States.AUTH_FAILED);
sendAuthEvent = true;
} else {
if (authState == KeeperState.SaslAuthenticated) {
sendAuthEvent = true;
}
}
}
if (sendAuthEvent) {
eventThread.queueEvent(new WatchedEvent(Watcher.Event.EventType.None, authState, null));
if (state == States.AUTH_FAILED) {
eventThread.queueEventOfDeath();
}
}
}
// 下一次查询超时时间
to = readTimeout - clientCnxnSocket.getIdleRecv();
} else {
// 递减连接超时时间
to = connectTimeout - clientCnxnSocket.getIdleRecv();
}
// 如果会话超时,包括连接超时
if (to <= 0) {
String warnInfo = String.format(