之前完成的项目作用zookeeper用作服务发现,抽象出注册和监听功能,供其他项目使用。对zookeeper的一些细节作个记录吧。
我们知道通过Zookeeper对象实例实现对zookeeper服务的调用。首先的问题就是消息的发送和接受,以及事件的回调。这里我们把目光转向ClientCnxn
创建实例
public ClientCnxn(String chrootPath, HostProvider hostProvider, int sessionTimeout, ZooKeeper zooKeeper,
ClientWatchManager watcher, ClientCnxnSocket clientCnxnSocket,
long sessionId, byte[] sessionPasswd, boolean canBeReadOnly) {
....
/**
* send线程完成发送request,接受response,生成event
* event线程派发event
*/
sendThread = new SendThread(clientCnxnSocket);
eventThread = new EventThread();
}
`SendThread线程`
public void run() {
//最大心跳间隔
final int MAX_SEND_PING_INTERVAL = 10000;
while (state.isAlive()) {
try {
//1、如果末连接,随机休眠1~1000,根据closing(用户请求)决定是跳出还是重连
//2、如果需要登录,进行登录请求,根据结果生成事件
if (zooKeeperSaslClient != null) {
。。。
if (sendAuthEvent == true) {
eventThread.queueEvent(new WatchedEvent(
Watcher.Event.EventType.None,
authState,null));
}
}
//3、如果waitTimeOut超时,抛出SessionTimeoutException
if (to <= 0) {
throw new SessionTimeoutException(...);
}
//4、心跳
sendPing()
//5、如果是只读模式
pingRwServer();
//6、开始读、写IO
clientCnxnSocket.doTransport(to, pendingQ, outgoingQ, ClientCnxn);
} catch (Throwable e) {
//发送Disconnected事件
if (state.isAlive()) {
eventThread.queueEvent(new WatchedEvent(
Event.EventType.None,
Event.KeeperState.Disconnected,
null));
}
}
}
//如果alive状态发送Disconnected事件
if (state.isAlive()) {
eventThread.queueEvent(new WatchedEvent(Event.EventType.None,
Event.KeeperState.Disconnected, null));
}
}
`SendThread线程`
void readResponse(ByteBuffer incomingBuffer) throws IOException {
//心跳响应
if (replyHdr.getXid() == -2) {
// -2 is the xid for pings
if (LOG.isDebugEnabled()) {
LOG.debug("Got ping response for sessionid: 0x"
+ Long.toHexString(sessionId)
+ " after "
+ ((System.nanoTime() - lastPingSentNs) / 1000000)
+ "ms");
}
return;
}
//认证响应
if (replyHdr.getXid() == -4) {
// -4 is the xid for AuthPacket
if(replyHdr.getErr() == KeeperException.Code.AUTHFAILED.intValue()) {
state = States.AUTH_FAILED;
eventThread.queueEvent( new WatchedEvent(Watcher.Event.EventType.None,
Watcher.Event.KeeperState.AuthFailed, null) );
}
if (LOG.isDebugEnabled()) {
LOG.debug("Got auth sessionid:0x"
+ Long.toHexString(sessionId));
}
return;
}
//通知
if (replyHdr.getXid() == -1) {
// -1 means notification
if (LOG.isDebugEnabled()) {
LOG.debug("Got notification sessionid:0x"
+ Long.toHexString(sessionId));
}
WatcherEvent event = new WatcherEvent();
event.deserialize(bbia, "response");
// convert from a server path to a client path
if (chrootPath != null) {
String serverPath = event.getPath();
if(serverPath.compareTo(chrootPath)==0)
event.setPath("/");
else if (serverPath.length() > chrootPath.length())
event.setPath(serverPath.substring(chrootPath.length()));
else {
LOG.warn("Got server path " + event.getPath()
+ " which is too short for chroot path "
+ chrootPath);
}
}
WatchedEvent we = new WatchedEvent(event);
if (LOG.isDebugEnabled()) {
LOG.debug("Got " + we + " for sessionid 0x"
+ Long.toHexString(sessionId));
}
eventThread.queueEvent( we );
return;
}
...
finishPacket(packet);
}
`EventThread`
public void run() {
try {
isRunning = true;
while (true) {
Object event = waitingEvents.take();
if (event == eventOfDeath) {
wasKilled = true;
} else {
processEvent(event);
}
if (wasKilled)
synchronized (waitingEvents) {
if (waitingEvents.isEmpty()) {
isRunning = false;
break;
}
}
}
} catch (InterruptedException e) {
LOG.error("Event thread exiting due to interruption", e);
}
LOG.info("EventThread shut down");
}