zk源码之客户端<2>

我们今天继续 来看这个doIo
上面我也说过了 doIo主要做的操作就是 . 连接成功了 代表我此时可以读数据了 也可以写数据了

 if (sockKey.isReadable()) {
    
    

            int rc = sock.read(incomingBuffer);
            if (rc < 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) {
    
      // 连接有没有初始化
                    // 没有初始化
                    System.out.println("没有初始化");
                    readConnectResult();  // 读取连接的结果
                    enableRead();
                    if (findSendablePacket(outgoingQueue,
                            cnxn.sendThread.clientTunneledAuthenticationInProgress()) != null) {
    
    
                        // Since SASL authentication has completed (if client is configured to do so),
                        // outgoing packets waiting in the outgoingQueue can now be sent.
                        enableWrite();
                    }
                    lenBuffer.clear();
                    incomingBuffer = lenBuffer;
                    updateLastHeard();
                    initialized = true;
                } else {
    
    
                    sendThread.readResponse(incomingBuffer);
                    lenBuffer.clear();
                    incomingBuffer = lenBuffer;
                    updateLastHeard();
                }
            }
        }
        if (sockKey.isWritable()) {
    
    // 写就绪
            synchronized(outgoingQueue) {
    
    

                Packet p = findSendablePacket(outgoingQueue,
                        cnxn.sendThread.clientTunneledAuthenticationInProgress());
                if (p != null) {
    
    
                    Record request = p.request;
                    if(request!=null){
    
    
                        System.out.println(request.getClass());
                    }
                    System.out.println("package------>"+p.toString());
                    updateLastSend();
                    // If we already started writing p, p.bb will already exist
                    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);  //byteBuffer 数据 socket<--buffer  将数据发送到服务端

                    // 发送完成后,从发送队列移除该Packet并将其加入到pendingQueue等待服务器的响应
                    if (!p.bb.hasRemaining()) {
    
    
                        sentCount++;
                        outgoingQueue.removeFirstOccurrence(p);
                        if (p.requestHeader != null
                                && p.requestHeader.getType() != OpCode.ping
                                && p.requestHeader.getType() != OpCode.auth) {
    
    
                            synchronized (pendingQueue) {
    
    
                                pendingQueue.add(p);//什么时候拿到结果呢,读结果
                            }
                        }
                    }
                }
                if (outgoingQueue.isEmpty()) {
    
    
                    // No more packets to send: turn off write interest flag.
                    // Will be turned on later by a later call to enableWrite(),
                    // from within ZooKeeperSaslClient (if client is configured
                    // to attempt SASL authentication), or in either doIO() or
                    // in doTransport() if not.
                    disableWrite();
                } else if (!initialized && p != null && !p.bb.hasRemaining()) {
    
    
                    // On initial connection, write the complete connect request
                    // packet, but then disable further writes until after
                    // receiving a successful connection response.  If the
                    // session is expired, then the server sends the expiration
                    // response and immediately closes its end of the socket.  If
                    // the client is simultaneously writing on its end, then the
                    // TCP stack may choose to abort with RST, in which case the
                    // client would never receive the session expired event.  See
                    // http://docs.oracle.com/javase/6/docs/technotes/guides/net/articles/connection_release.html
                    disableWrite();
                } else {
    
    
                    // Just in case
                    enableWrite();
                }
            }
        }
1.pendingQueue是等待服务器返回结果的Packet,意思是你把这些packet通过socket 发送到服务端, 但是还没有结果,
2. sockKey.isReadable()    //可以从socket channel中读数据 也就是说服务器返回数据了 

在这里插入图片描述

initialized 这个值默认是false 的 也就是说从服务端读数据第一次返回数据是经过这个分支的

在这里插入图片描述

我们看下这个 org.apache.zookeeper.ClientCnxn.SendThread#primeConnection 这个方法
就是说在连接的时候,不管当时是连接成功还是后来连接成功,  他都会构造 一个ConnectRequest 对象 并且加入到 outgoingQueue 队列里面去,他此时要告诉zk服务端,我要来连接了
所以在服务端返回数据第第一次 会返回到 【!initialized)】  这个判断了面 

在这里插入图片描述

所以说当第一次read数据的时候,他会拿连接的结果,
 sendThread.onConnected(conRsp.getTimeOut(), this.sessionId,
                conRsp.getPasswd(), isRO);
                然后再进行这个方法
  eventThread.queueEvent(new WatchedEvent(
                    Watcher.Event.EventType.None,
                    eventState, null));
//  主要看这个
也就是说 在连接的时候,客户端先给服务器发送一个连接请求,
 服务端也告诉你连接成功了
并且会在客户端本地触发一个EventType.None 的事件
  initialized = true; 然后就会把这个状态改位true 当第二次read数据就不会进这个if判断了
  然后下一步操作就会一直sendPing命令

在这里插入图片描述

当我们真正发送  create 命令的时候 zk 的responser

在这里插入图片描述

会进入到这个分支  代表此时我接受到服务器的返回数据了

在这里插入图片描述

我们再回来看看发Ping命令这段逻辑 在发Ping的时候
 RequestHeader h = new RequestHeader(-2, OpCode.ping); 他将xid  置为-2, 
然后回来看
      sendThread.readResponse(incomingBuffer); 这里
      

在这里插入图片描述

如果返回的 xid=-2  代表发送的是ping命令 他在这里写死了
    packet = pendingQueue.remove();//  之后我们会消费oendingQueue中的packet

在这里插入图片描述

 在这一步的时候我们就已经拿到全部的返回数据了,
 org.apache.zookeeper.ClientCnxn#finishPacket
  然后我们看 finishPacket中的逻辑
  他在这里主要做了一个事情就是notieyAll()
  就可以唤醒之前的wait了 
  
  
  

在这里插入图片描述

总结下 zk的客户端流程
1,zk启动时候会通过nio的方式连接服务器, 如果没有连接成功,会触发重试机制
2。如果socket连接成功了,会给服务器 发送一个connect的请求,并且服务器会响应一个connectResonse,然后就是不断的发Ping命令
3.当我们cmd 命令行 输入creat命令的时候,zk 会把这些命令封装成packet 然后放到outgoingQueue中去进行异步消费,

Guess you like

Origin blog.csdn.net/weixin_43689953/article/details/116375964