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命令的存储类)

MyCommandOptionsZooKeeperMain的静态内部类,看它的构造函数可以知道,启动客户端不带任何参数时连接的是本地的服务端,默认超时为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)算出来的
最后初始化了两个非常重要的线程SendThreadEventThread,都是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();
就是启动线程SendThreadEventThread

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);
    }
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值