zookeeper客户端源码深入分析
从启动脚本寻找入口
zookeeper时纯java编写的,不管是zkCli.cmd
还是zkCli.sh
脚本,都有org.apache.zookeeper.ZooKeeperMain
例如zkCli.cmd
脚本文件
setlocal
call "%~dp0zkEnv.cmd"
set ZOOMAIN=org.apache.zookeeper.ZooKeeperMain
java "-Dzookeeper.log.dir=%ZOO_LOG_DIR%" "-Dzookeeper.root.logger=%ZOO_LOG4J_PROP%" -cp "%CLASSPATH%" %ZOOMAIN% %*
endlocal
因此可以肯定zookeeper客户端是从ZooKeeperMain开始的
ZooKeeperMain入口
public static void main(String args[])
throws KeeperException, IOException, InterruptedException
{
// args就是命令行zkCli后面带的参数
ZooKeeperMain main = new ZooKeeperMain(args);
main.run();
}
public ZooKeeperMain(String args[]) throws IOException, InterruptedException {
// 解析命令行参数
cl.parseOptions(args);
System.out.println("Connecting to " + cl.getOption("server"));
connectToZK(cl.getOption("server"));
}
MyCommandOptions(shell命令的存储类)
MyCommandOptions是ZooKeeperMain的静态内部类,看它的构造函数可以知道,启动客户端不带任何参数时连接的是本地的服务端,默认超时为30s
public MyCommandOptions() {
options.put("server", "localhost:2181");
options.put("timeout", "30000");
}
客户端连接时解析命令行参数
public boolean parseOptions(String[] args) {
List<String> argList = Arrays.asList(args);
Iterator<String> it = argList.iterator();
while (it.hasNext()) {
String opt = it.next();
try {
if (opt.equals("-server")) {// // 连接服务端的地址
options.put("server", it.next());
} else if (opt.equals("-timeout")) {// 设置超时时长
options.put("timeout", it.next());
} else if (opt.equals("-r")) {// 设置是否只读
options.put("readonly", "true");
}
} catch (NoSuchElementException e){
System.err.println("Error: no argument found for option "
+ opt);
return false;
}
if (!opt.startsWith("-")) {
command = opt;
cmdArgs = new ArrayList<String>( );
cmdArgs.add( command );
while (it.hasNext()) {
cmdArgs.add(it.next());
}
return true;
}
}
return true;
}
connectToZK(cl.getOption("server"));
连接ZooKeeper
protected void connectToZK(String newHost) throws InterruptedException, IOException {
if (zk != null && zk.getState().isAlive()) {
zk.close();
}
host = newHost;
boolean readOnly = cl.getOption("readonly") != null;
zk = new ZooKeeper(host,
Integer.parseInt(cl.getOption("timeout")),
new MyWatcher(), readOnly);
}
这里就跟原生ZooKeeper连接服务端是一样的
ZooKeeper
public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher,
boolean canBeReadOnly)
throws IOException
{
LOG.info("Initiating client connection, connectString=" + connectString
+ " sessionTimeout=" + sessionTimeout + " watcher=" + watcher);
watchManager.defaultWatcher = watcher;
// 包装地址,可以传多个(以逗号隔开)
ConnectStringParser connectStringParser = new ConnectStringParser(
connectString);
// 也是包装地址,打断顺序
HostProvider hostProvider = new StaticHostProvider(
connectStringParser.getServerAddresses());
cnxn = new ClientCnxn(connectStringParser.getChrootPath(),
hostProvider, sessionTimeout, this, watchManager,
getClientCnxnSocket(), canBeReadOnly);
cnxn.start();
}
ClientCnxn
用于连接客户端的socket
理解为zookeeper客户端,其中getClientCnxnSocket()
获取一个socket连接对象
可以设置系统属性zookeeper.clientCnxnSocket
的值,该值为一个类必须继承ClientCnxnSocket
如果为空,则采用nio的实现ClientCnxnSocketNIO
private static ClientCnxnSocket getClientCnxnSocket() throws IOException {
String clientCnxnSocketName = System
.getProperty(ZOOKEEPER_CLIENT_CNXN_SOCKET);
if (clientCnxnSocketName == null) {
// 默认采用nio实现
clientCnxnSocketName = ClientCnxnSocketNIO.class.getName();
}
try {
return (ClientCnxnSocket) Class.forName(clientCnxnSocketName).getDeclaredConstructor()
.newInstance();
} catch (Exception e) {
IOException ioe = new IOException("Couldn't instantiate "
+ clientCnxnSocketName);
ioe.initCause(e);
throw ioe;
}
}
ClientCnxn的构造函数
看一下ClientCnxn的构造函数
connectTimeout
连接超时与readTimeout
读取超时是根据设置的超时时长(默认30s)算出来的
最后初始化了两个非常重要的线程SendThread与EventThread,都是ClientCnxn的内部类
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;// 默认为0
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();// 监听事件线程
}
启动两个线程SendThread与EventThread
然后执行cnxn.start();
就是启动线程SendThread与EventThread
public void start() {
// 发送接收数据
sendThread.start();
// 处理监听器
eventThread.start();
}
SendThread
关注run()
方法,只要连接一直存活(没有关闭或验权失败),线程就一直运行
clientCnxnSocket
就是一开始实例化的socket对象,建立socket连接并发送数据
while (state.isAlive()) {
try {
if (!clientCnxnSocket.isConnected()) {// 如果socket没有连接
if(!isFirstConnect){// 不是第一次连接
try {
// 不是第一次连接,再次连接时会先睡眠1s
Thread.sleep(r.nextInt(1000));
} catch (InterruptedException e) {
LOG.warn("Unexpected exception", e);
}
}
// 如果连接关闭,跳出循环
if (closing || !state.isAlive()) {
break;
}
// 读写服务端地址,暂时没有就从hostProvider中取一个
if (rwServerAddress != null) {
serverAddress = rwServerAddress;
rwServerAddress = null;
} else {
// 获取服务端地址,类似轮询的方式(一种算法)
serverAddress = hostProvider.next(1000);
}
// 建立连接,socket建立成功后会调用SendThread.primeConnection()
startConnect(serverAddress);
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);
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));
}
}
// 如果连接成功,看是否超过了readTime
to = readTimeout - clientCnxnSocket.getIdleRecv();
} else {
// 连接时间
to = connectTimeout - clientCnxnSocket.getIdleRecv();
}
// 超时了则抛异常SessionTimeoutException,但这个异常会被上层catch住,尝试重连
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);
}
if (state.isConnected()) {
// 如果连接成功,就不断地ping
//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) {
// 进行ping,把ping包也加入到outgoingQueue中
sendPing();
clientCnxnSocket.updateLastSend();
} else {
if (timeToNextPing < to) {
to = timeToNextPing;
}
}
}
// If we are in read-only mode, seek for read/write server
// client连接了只读的server时,不断根据hostProvider找到一个可读写的server
if (state == States.CONNECTEDREADONLY) {
long now = Time.currentElapsedTime();
int idlePingRwServer = (int) (now - lastPingRwServer);
if (idlePingRwServer >= pingRwTimeout) {
lastPingRwServer = now;
idlePingRwServer = 0;
pingRwTimeout =
Math.min(2*pingRwTimeout, maxPingRwTimeout);
// ping读写的server
pingRwServer();
}
to = Math.min(to, pingRwTimeout - idlePingRwServer);
}
// 进行传输,传输的是outgoingQueue队列中的Packet
clientCnxnSocket.doTransport(to, pendingQueue, outgoingQueue, ClientCnxn.this);
}
ClientCnxnSocketNIO
startConnect建立连接
startConnect(serverAddress);
开始建立连接,执行clientCnxnSocket.connect(addr);
,clientCnxnSocket
就是ClientCnxnSocketNIO
void connect(InetSocketAddress addr) throws IOException {
// 建立socket,channel
SocketChannel sock = createSock();
try {
// 注册这个sock到服务端
registerAndConnect(sock, addr);
} catch (IOException e) {
LOG.error("Unable to open socket to " + addr);
sock.close();
throw e;
}
initialized = false;
/*
* Reset incomingBuffer
*/
lenBuffer.clear();
incomingBuffer = lenBuffer;
}
void registerAndConnect(SocketChannel sock, InetSocketAddress addr) throws IOException {
// 将socketChannel注册到selector上,并且监听连接事件
sockKey = sock.register(selector, SelectionKey.OP_CONNECT);
// registerAndConnect中如果立即connect就调用sendThread.primeConnection();
// 如果没有立即connect上,那么就在下面介绍的doTransport中等待SocketChannel finishConnect再调用
boolean immediateConnect = sock.connect(addr);
if (immediateConnect) {
// 建立连接的核心方法
sendThread.primeConnection();
}
}
doTransport发送数据
clientCnxnSocket.doTransport(to, pendingQueue, outgoingQueue, ClientCnxn.this);
开始传输数据
// outgoingQueue 是请求发送队列,是client存储需要被发送到server端的Packet队列
// pendingQueue是已经从client发送,但是要等待server响应的packet队列
@Override
void doTransport(int waitTimeOut, List<Packet> pendingQueue, LinkedList<Packet> outgoingQueue,
ClientCnxn cnxn)
throws IOException, InterruptedException {
selector.select(waitTimeOut);
Set<SelectionKey> selected;
synchronized (this) {
selected = selector.selectedKeys();
}
// Everything below and until we get back to the select is
// non blocking, so time is effectively a constant. That is
// Why we just have to do this once, here
updateNow();
for (SelectionKey k : selected) {
SocketChannel sc = ((SocketChannel) k.channel());
// 如果就绪的是connect事件,这个出现在registerAndConnect函数没有立即连接成功的情况
if ((k.readyOps() & SelectionKey.OP_CONNECT) != 0) {
if (sc.finishConnect()) {
updateLastSendAndHeard();
// 建立连接的核心方法
sendThread.primeConnection();
}
} else if ((k.readyOps() & (SelectionKey.OP_READ | SelectionKey.OP_WRITE)) != 0) { // 如果就绪的是读或者写事件
// 若读写就绪,调用doIO函数
doIO(pendingQueue, outgoingQueue, cnxn);
}
}
if (sendThread.getZkState().isConnected()) {
synchronized(outgoingQueue) {
if (findSendablePacket(outgoingQueue,
cnxn.sendThread.clientTunneledAuthenticationInProgress()) != null) { // 如果有可以发送的packet
enableWrite();
}
}
}
selected.clear();
}
doIO读写数据
void doIO(List<Packet> pendingQueue, LinkedList<Packet> outgoingQueue, ClientCnxn cnxn)
throws InterruptedException, IOException {
SocketChannel sock = (SocketChannel) sockKey.channel();
if (sock == null) {
throw new IOException("Socket is null!");
}
// 可以读了
if (sockKey.isReadable()) { // 可以读了
int rc = sock.read(incomingBuffer);
if (rc < 0) { //如果<0,表示读到末尾了,这种情况出现在连接关闭的时候
throw new EndOfStreamException(
"Unable to read additional data from server sessionid 0x"
+ Long.toHexString(sessionId)
+ ", likely server has closed socket");
}
if (!incomingBuffer.hasRemaining()) {
incomingBuffer.flip();
if (incomingBuffer == lenBuffer) {
recvCount++;
readLength();
} else if (!initialized) { // 如果client和server的连接还没有初始化
readConnectResult(); //读取connect 回复
enableRead();
if (findSendablePacket(outgoingQueue,
cnxn.sendThread.clientTunneledAuthenticationInProgress()) != null) {
enableWrite(); //允许写,因为有要发送的packet
}
lenBuffer.clear();
incomingBuffer = lenBuffer;
updateLastHeard();
initialized = true;
} else { //如果已连接,并且已经给incomingBuffer分配了对应len的空间
// 读取响应
sendThread.readResponse(incomingBuffer);
lenBuffer.clear();
incomingBuffer = lenBuffer;
updateLastHeard();
}
}
}
// 可以写了
if (sockKey.isWritable()) {
synchronized(outgoingQueue) {
// 找到可以发送的Packet
Packet p = findSendablePacket(outgoingQueue,
cnxn.sendThread.clientTunneledAuthenticationInProgress());
if (p != null) {
updateLastSend();
//如果packet还没有生成byteBuffer,那就生成byteBuffer
if (p.bb == null) {
if ((p.requestHeader != null) &&
(p.requestHeader.getType() != OpCode.ping) &&
(p.requestHeader.getType() != OpCode.auth)) {
p.requestHeader.setXid(cnxn.getXid());
}
p.createBB();
}
sock.write(p.bb);// 发送服务端
if (!p.bb.hasRemaining()) { // 没有剩下的了
sentCount++;
outgoingQueue.removeFirstOccurrence(p); // 从待发送队列中取出该packet
if (p.requestHeader != null
&& p.requestHeader.getType() != OpCode.ping
&& p.requestHeader.getType() != OpCode.auth) {
synchronized (pendingQueue) {
pendingQueue.add(p); // 加入待回复的队列
}
}
}
}
if (outgoingQueue.isEmpty()) {
disableWrite(); // 如果没有要发的,就禁止写
} else if (!initialized && p != null && !p.bb.hasRemaining()) {
disableWrite();
} else {
// Just in case
enableWrite();
}
}
}
}
回到ZooKeeperMain
run()打开命令窗口
执行run();
打开cmd命令窗口,没有写入cmd命令时
void run() throws KeeperException, IOException, InterruptedException {
// 没有写入cmd命令
if (cl.getCommand() == null) {
System.out.println("Welcome to ZooKeeper!");
boolean jlinemissing = false;
// only use jline if it's in the classpath
try {
Class<?> consoleC = Class.forName("jline.ConsoleReader");
Class<?> completorC =
Class.forName("org.apache.zookeeper.JLineZNodeCompletor");
System.out.println("JLine support is enabled");
Object console =
consoleC.getConstructor().newInstance();
Object completor =
completorC.getConstructor(ZooKeeper.class).newInstance(zk);
Method addCompletor = consoleC.getMethod("addCompletor",
Class.forName("jline.Completor"));
addCompletor.invoke(console, completor);
String line;
Method readLine = consoleC.getMethod("readLine", String.class);
// 循环等待命令行操作
while ((line = (String)readLine.invoke(console, getPrompt())) != null) {
// 执行命令行
executeLine(line);
}
} catch (ClassNotFoundException e) {
LOG.debug("Unable to start jline", e);
jlinemissing = true;
} catch (NoSuchMethodException e) {
LOG.debug("Unable to start jline", e);
jlinemissing = true;
} catch (InvocationTargetException e) {
LOG.debug("Unable to start jline", e);
jlinemissing = true;
} catch (IllegalAccessException e) {
LOG.debug("Unable to start jline", e);
jlinemissing = true;
} catch (InstantiationException e) {
LOG.debug("Unable to start jline", e);
jlinemissing = true;
}
if (jlinemissing) {
System.out.println("JLine support is disabled");
BufferedReader br =
new BufferedReader(new InputStreamReader(System.in));
String line;
while ((line = br.readLine()) != null) {
executeLine(line);
}
}
} else {
// 执行cmd命令
processCmd(cl);
}
}
看截图,确实打印了上面代码System.out.println("Welcome to ZooKeeper!");
while等待cmd命令
while ((line = (String)readLine.invoke(console, getPrompt())) != null) {
// 执行命令行
executeLine(line);
}
其中getPrompt()
,就是上图中的 [zk: localhost:2181(CONNECTED) 0]
protected String getPrompt() {
return "[zk: " + host + "("+zk.getState()+")" + " " + commandCount + "] ";
}
executeLine(line)执行命令行语句
public void executeLine(String line)throws InterruptedException, IOException, KeeperException {
if (!line.equals("")) {
// 校验命令行语句
cl.parseCommand(line);
// 添加到历史纪录,可以执行history擦看
addToHistory(commandCount,line);
// 处理命令行语句
processCmd(cl);
// 执行次数+1
commandCount++;
}
}
processCmd(cl);
处理命令行语句,return processZKCmd(co);
,对语句进行判断,然后执行不同的方法
protected boolean processZKCmd(MyCommandOptions co)
throws KeeperException, IOException, InterruptedException
{
Stat stat = new Stat();
String[] args = co.getArgArray();
String cmd = co.getCommand();
if (args.length < 1) {
usage();
return false;
}
if (!commandMap.containsKey(cmd)) {
usage();
return false;
}
boolean watch = args.length > 2;
String path = null;
List<ACL> acl = Ids.OPEN_ACL_UNSAFE;
LOG.debug("Processing " + cmd);
if (cmd.equals("quit")) {
System.out.println("Quitting...");
zk.close();
System.exit(0);
} else if
.
.
.
if (cmd.equals("create") && args.length >= 3) {
int first = 0;
// 构造CreateMode,从参数里面解析出节点类型
CreateMode flags = CreateMode.PERSISTENT;
if ((args[1].equals("-e") && args[2].equals("-s"))
|| (args[1]).equals("-s") && (args[2].equals("-e"))) {
first+=2;
flags = CreateMode.EPHEMERAL_SEQUENTIAL;
} else if (args[1].equals("-e")) {
first++;
flags = CreateMode.EPHEMERAL;
} else if (args[1].equals("-s")) {
first++;
flags = CreateMode.PERSISTENT_SEQUENTIAL;
}
if (args.length == first + 4) {
acl = parseACLs(args[first+3]);
}
path = args[first + 1];
String newPath = zk.create(path, args[first+2].getBytes(), acl,
flags);
System.err.println("Created " + newPath);
} else if
.
.
.
return watch;
}
create命令
例如执行create
命令
String newPath = zk.create(path, args[first+2].getBytes(), acl, flags);
System.err.println("Created " + newPath);
ZooKeeper.create创建节点命令
public String create(final String path, byte data[], List<ACL> acl, CreateMode createMode)
throws KeeperException, InterruptedException
{
final String clientPath = path;
// 对节点路径进行校验
PathUtils.validatePath(clientPath, createMode.isSequential());
final String serverPath = prependChroot(clientPath);
RequestHeader h = new RequestHeader();
// 操作类型
h.setType(ZooDefs.OpCode.create);
CreateRequest request = new CreateRequest();
CreateResponse response = new CreateResponse();
// 请求内容
request.setData(data);
request.setFlags(createMode.toFlag());
// 操作节点路径
request.setPath(serverPath);
if (acl != null && acl.size() == 0) {
throw new KeeperException.InvalidACLException();
}
// 操作权限
request.setAcl(acl);
// 提交请求,等待返回
ReplyHeader r = cnxn.submitRequest(h, request, response, null);
if (r.getErr() != 0) {
// 请求失败
throw KeeperException.create(KeeperException.Code.get(r.getErr()),
clientPath);
}
if (cnxn.chrootPath == null) {
return response.getPath();
} else {
// 返回请求成功的节点路径
return response.getPath().substring(cnxn.chrootPath.length());
}
}
ClientCnxn.submitRequest(提交请求后wait)
cnxn.submitRequest(h, request, response, null);
,这里客户端提交数据后,一直等待服务端返回。
// 同步获取结果
public ReplyHeader submitRequest(RequestHeader h, Record request,
Record response, WatchRegistration watchRegistration)
throws InterruptedException {
ReplyHeader r = new ReplyHeader();
// 将数据封装进Packet并放入队列outgoingQueue中,等待线程SendThread处理
Packet packet = queuePacket(h, r, request, response, null, null, null,
null, watchRegistration);
synchronized (packet) {
// finished不为true时一直等待
while (!packet.finished) {
// 如果packet没有处理完,则当前线程一直等待
packet.wait();
}
}
return r;
}
SendThread.readResponse(接收响应后notifyAll)
在上面有注释,执行到ClientCnxnSocketNIO.doIO
时,会执行sendThread.readResponse(incomingBuffer);
,处理完会将packet唤醒
// 读取响应
void readResponse(ByteBuffer incomingBuffer) throws IOException {
ByteBufferInputStream bbis = new ByteBufferInputStream(
incomingBuffer);
BinaryInputArchive bbia = BinaryInputArchive.getArchive(bbis);
ReplyHeader replyHdr = new ReplyHeader();
replyHdr.deserialize(bbia, "header");
// 判断响应类型
if (replyHdr.getXid() == -2) { // ping 的结果
// -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) { // watch事件通知
// -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));
}
// 把通知事件加到waitingEvents
eventThread.queueEvent( we );
return;
}
// If SASL authentication is currently in progress, construct and
// send a response packet immediately, rather than queuing a
// response as with other packets.
if (clientTunneledAuthenticationInProgress()) {
GetSASLRequest request = new GetSASLRequest();
request.deserialize(bbia,"token");
zooKeeperSaslClient.respondToServer(request.getToken(),
ClientCnxn.this);
return;
}
Packet packet;
synchronized (pendingQueue) {
if (pendingQueue.size() == 0) {
throw new IOException("Nothing in the queue, but got "
+ replyHdr.getXid());
}
// 从队列pendingQueue中拿到处理完的packet
packet = pendingQueue.remove();
}
/*
* Since requests are processed in order, we better get a response
* to the first request!
*/
try {
// 如果请求和响应的顺序不一样,会报错
if (packet.requestHeader.getXid() != replyHdr.getXid()) {
packet.replyHeader.setErr(
KeeperException.Code.CONNECTIONLOSS.intValue());
throw new IOException("Xid out of order. Got Xid "
+ replyHdr.getXid() + " with err " +
+ replyHdr.getErr() +
" expected Xid "
+ packet.requestHeader.getXid()
+ " for a packet with details: "
+ packet );
}
packet.replyHeader.setXid(replyHdr.getXid());
// 把服务的报错信息设置进来
packet.replyHeader.setErr(replyHdr.getErr());
packet.replyHeader.setZxid(replyHdr.getZxid());
if (replyHdr.getZxid() > 0) {
lastZxid = replyHdr.getZxid();
}
if (packet.response != null && replyHdr.getErr() == 0) {
// 将响应放入packet
packet.response.deserialize(bbia, "response");
}
if (LOG.isDebugEnabled()) {
LOG.debug("Reading reply sessionid:0x"
+ Long.toHexString(sessionId) + ", packet:: " + packet);
}
} finally {
// 处理packet
finishPacket(packet);
}
}
处理packet
private void finishPacket(Packet p) {
if (p.watchRegistration != null) {
p.watchRegistration.register(p.replyHeader.getErr());
}
if (p.cb == null) {
// 操作同步通知
synchronized (p) {
// finished设置为true
p.finished = true;
// 唤醒当前packet
p.notifyAll();
}
} else {
// 操作异步通知,添加监听器
p.finished = true;
eventThread.queuePacket(p);
}
}