zk的请求和响应是通过id对应上的:

请求头(RequestHeader)和响应头(ReplyHeader)共用一个xid,它的本质是ClientCnxn类中的一个计数器。

1. 首先看客户端:

Packet类封装了客户端的请求头、请求体、响应头、响应体。

org.apache.zookeeper.ClientCnxn.Packet

static class Packet {
    RequestHeader requestHeader;
    ReplyHeader replyHeader;
    Record request;
    Record response;
    ByteBuffer bb;
    /** Client's view of the path (may differ due to chroot) **/
    String clientPath;
    /** Servers's view of the path (may differ due to chroot) **/
    String serverPath;
    boolean finished;
    AsyncCallback cb;
    Object ctx;
    WatchRegistration watchRegistration;
    public boolean readOnly;

    Packet(RequestHeader requestHeader, ReplyHeader replyHeader,
           Record request, Record response,
           WatchRegistration watchRegistration) {
        this(requestHeader, replyHeader, request, response,
             watchRegistration, false);
    }

    Packet(RequestHeader requestHeader, ReplyHeader replyHeader,
           Record request, Record response,
           WatchRegistration watchRegistration, boolean readOnly) {
        this.requestHeader = requestHeader;
        this.replyHeader = replyHeader;
        this.request = request;
        this.response = response;
        this.readOnly = readOnly;
        this.watchRegistration = watchRegistration;
    }

    public void createBB() {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos);
            boa.writeInt(-1, "len"); // We'll fill this in later
            if (requestHeader != null) {
                requestHeader.serialize(boa, "header");
            }
            if (request instanceof ConnectRequest) {
                request.serialize(boa, "connect");
                // append "am-I-allowed-to-be-readonly" flag
                boa.writeBool(readOnly, "readOnly");
            } else if (request != null) {
                request.serialize(boa, "request");
            }
            baos.close();
            this.bb = ByteBuffer.wrap(baos.toByteArray());
            this.bb.putInt(this.bb.capacity() - 4);
            this.bb.rewind();
        } catch (IOException e) {
            LOG.warn("Ignoring unexpected exception", e);
        }
    }
}

RequestHeader

public class RequestHeader implements Record {
  private int xid;
  private int type;
}

GetDataRequest

public class GetDataRequest implements Record {
  private String path;
  private boolean watch;
}

ReplyHeader

public class ReplyHeader implements Record {
  private int xid;
  private long zxid;
  private int err;
}

GetDataResponse

public class GetDataResponse implements Record {
  private byte[] data;
  private org.apache.zookeeper.data.Stat stat;
}

客户端发送请求:

//ClientCnxnSocketNIO
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.isWritable()) {
        synchronized(outgoingQueue) {
            Packet p = findSendablePacket(outgoingQueue,
                    cnxn.sendThread.clientTunneledAuthenticationInProgress());
            if (p != null) {
                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)) {
                        //设置连接id
                        p.requestHeader.setXid(cnxn.getXid());
                    }
                    //创建发送的ByteBuffer
                    p.createBB();
                }
                sock.write(p.bb);
                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()) {
                disableWrite();
            } else if (!initialized && p != null && !p.bb.hasRemaining()) {
                disableWrite();
            } else {
                enableWrite();
            }
        }
    }
}
//ClientCnxn类
private int xid = 1;
synchronized public int getXid() {
    return xid++;
}

2. 然后看服务端:

服务端用Request类封装了请求的相关信息,

//org.apache.zookeeper.server.Request
public Request(ServerCnxn cnxn, long sessionId, int xid, int type,
        ByteBuffer bb, List<Id> authInfo) {
    this.cnxn = cnxn;
    this.sessionId = sessionId;
    //对应RequestHeader的xid
    this.cxid = xid; 
    this.type = type;
    this.request = bb;
    this.authInfo = authInfo;
}

在ZooKeeperServer.processPacket方法中有:

//反序列化获取RequestHeader 
RequestHeader h = new RequestHeader();
h.deserialize(bia, "header");

//把RequestHeader的xid赋给Request的cxid
Request si = new Request(cnxn, cnxn.getSessionId(), h.getXid(), h.getType(), 
               incomingBuffer, cnxn.getAuthInfo());

在FinalRequestProcessor.processRequest方法中有:

long lastZxid = zks.getZKDatabase().getDataTreeLastProcessedZxid();
//xid从RequstHeader传到Request,然后传到ReplyHeader
ReplyHeader hdr = new ReplyHeader(request.cxid, lastZxid, err.intValue());

 

posted on 2018-01-29 15:03  偶尔发呆  阅读(439)  评论(0编辑  收藏  举报