读《从Paxos到Zookeeper 分布式一致性原理与实践》笔记之通信协议

1.    Zookeeper技术内幕

1.1. 通信协议

        基于TCP/IP协议,zookeeper实现了自己的通信协议来完成客户端与服务端、服务端与服务端之间的网络通信。Zookeeper通信协议整体上的设计非常简单,对于请求,主要包含请求头和请求体,而对于响应,则主要包含响应头和响应体。


1.1.1.  协议解析:请求部分

        GetDataRequest“获取节点数据”请求的完整协议定义

 

请求长度

请求头

请求体

字节偏移量

0 - 3

4 - 11

12 - n

4 - 7

8 - 11

12 - 15

16-(n-1)

n

协议内容

len

xid

type

len(数据体长度)

path

watch

请求头:RequestHeader

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

        xid用于记录请求发起的先后序号,用于确定单个客户端请求的响应顺序。Type代表请求的操作类型,常见的包括创建节点(OpCode.create)、删除节点(OpCode.delete)和获取节点数据(OpCode.getData)等。

请求体:Request

        协议的请求体部分是指请求的主体内容部分,包含了请求的所有操作内容。不同的请求类型,其请求体部分的结构是不同的,下面以获取节点数据的请求体为例来对请求体进行分析。

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

        该请求体包含了数据节点的节点路径path和是否注册Watcher的标识watch。

        序列化请求协议到缓存中:

static class Packet {
    RequestHeader requestHeader;
    ReplyHeader replyHeader;
    Record request;
    Record response;
    ByteBuffer bb;
    ......
    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 != 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);
        }
    }
}

1.1.2.  协议解析:响应部分

        以GetDataResponse“获取节点数据”响应为例,解析完整协议定义

 

请求长度

响应头

响应体

字节偏移量

0 - 3

4 - 19

20 - n

4 - 7

8 - 15

16 - 19

20 - 23

len

68

协议内容

len

xid

zxid

err

len

data

Stat

8

8

8

8

4

4

4

8

4

4

8

czxid

mzxid

ctime

mtime

version

cversion

aversion

ephemeral Owner

dataLength

numChildren

pzxid

响应头:ReplyHeader

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

        Xid和上文中提到的请求头中的xid是一致的,响应中只是将请求的xid原值返回。Zxid代表zookeeper服务器上当前最新的事物Id。Err则是一个错误码,当请求处理过程中出现异常情况时,会在这个错误码中表示出来,常见的包括处理成功(Code.OK)、节点不存在(Code.NONODE)和没有权限(Code.NOAUTH)等。

响应体:Request

        协议的响应体部分是指响应的主体内容部分,包含了相应的所有返回数据。不通的响应类型,其相应体部分的结构是不同的,下面以获取节点数据的响应体为例来对响应体进行分析。

public class GetDataResponse implements Record {
  private byte[] data;
  private org.apache.zookeeper.data.Stat stat;
}
public class Stat implements Record {
  private long czxid;
  private long mzxid;
  private long ctime;
  private long mtime;
  private int version;
  private int cversion;
  private int aversion;
  private long ephemeralOwner;
  private int dataLength;
  private int numChildren;
  private long pzxid;
}

        反序列化响应协议到缓存中:

class SendThread extends ZooKeeperThread {
    void readResponse(ByteBuffer incomingBuffer) throws IOException {
        ByteBufferInputStream bbis = new ByteBufferInputStream(incomingBuffer);
        BinaryInputArchive bbia = BinaryInputArchive.getArchive(bbis);
        ReplyHeader replyHdr = new ReplyHeader();
        replyHdr.deserialize(bbia, "header");
......
        Packet packet;
        synchronized (pendingQueue) {
            packet = pendingQueue.remove();
        }
        try {
            if (packet.requestHeader.getXid() != replyHdr.getXid()) {
                packet.replyHeader.setErr(
                        KeeperException.Code.CONNECTIONLOSS.intValue());
                throw new IOException("Xid out of order. Got Xid ");
            }
            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.response.deserialize(bbia, "response");
            }
        } finally {
            finishPacket(packet);
        }
    }
}


猜你喜欢

转载自blog.csdn.net/lihuayong/article/details/53705135