实例化zookeeper客户端后,客户端会创建ClientCnxn,其表示与服务端的连接交互对象。ClientCnxn将创建两个线程SendThread和EventThread线程。发送线程主要完成请求的发送和从服务端过来的应答的读取,对于读取的应答需要进一步处理的转由事件线程来处理。
客户端和服务端之间有session的概念,客户端随机连接到zookeeper server集群中的一台机器后,建立session后,会通过心跳的方式来维持该session。
这里先看一下心跳时间间隔。
public ClientCnxn(String chrootPath, HostProvider hostProvider, int sessionTimeout, ZooKeeper zooKeeper,
ClientWatchManager watcher, ClientCnxnSocket clientCnxnSocket,
long sessionId, byte[] sessionPasswd, boolean canBeReadOnly) {
this.zooKeeper = zooKeeper;
this.watcher = watcher;
this.sessionId = sessionId;
this.sessionPasswd = sessionPasswd;
this.sessionTimeout = sessionTimeout;
this.hostProvider = hostProvider;
this.chrootPath = chrootPath;
connectTimeout = sessionTimeout / hostProvider.size();
readTimeout = sessionTimeout * 2 / 3;
readOnly = canBeReadOnly;
sendThread = new SendThread(clientCnxnSocket);
eventThread = new EventThread();
}
从ClientCnxn构造可知,读超时时间设置为会话超时时间的2/3,实际上述构造时的session超时时间是客户端自己要求的超时时间,与服务端建立连接过程中,会与服务端协调一个超时时间,客户端最终使用的协调获得的超时时间。
SendThread线程运行过程中
if (state.isConnected()) {
int timeToNextPing = readTimeout / 2
- clientCnxnSocket.getIdleSend();
if (timeToNextPing <= 0) {
sendPing();
clientCnxnSocket.updateLastSend();
} else {
if (timeToNextPing < to) {
to = timeToNextPing;
}
}
}
计算时取读超时时间的一半,即会话超时的1/3。还需关注的是会话超时的判断逻辑,即在连接的状态下,客户端有2/3个会话周期没有收到服务端的消息后,即认为是会话时效。
if (state.isConnected()) {
//......
to = readTimeout - clientCnxnSocket.getIdleRecv();
} else {
to = connectTimeout - clientCnxnSocket.getIdleRecv();
}