您的位置:首页 > 其它

ZooKeeper系统模型之通信协议。

2018-02-11 10:52 302 查看
         基于TCP/IP协议,ZooKeeper实现了自己的通信协议来完成客户端与服务端、服务端与服务端之间的网络通信。ZooKeeper通信协议整体上的设计非常简单,对于请求,主要包含请求头和请求体,而对于响应,则主要包含响应头和响应体,如下图所示。



协议解析:请求部分

        我们首先来看请求协议的详细设计,下图定义了一个“获取节点数据”请求的完整协议定义。



        接下来,我们将从请求头和请求体两方面分别解析ZooKeeper请求的协议设计。

请求头:RequestHeader

        请求头中包含了请求最基本的信息,包括xid和type:





        xid用于记录客户端请求发起的先后顺序,用来确保单个客户端请求的响应顺序。type代表请求的操作类型,常见的包括创建节点(OpCode.create:1)、删除节点(OpCode.create:2)和获取节点数据(OpCode.getData:4)等,所有这些操作类型都被定义在类org.apache.zookeeper.ZooDefs.OpCode中。根据协议规定,除非是“会话创建”请求,其他所有的客户端请求中都会带上请求头。

请求体:Request

        协议的请求体部分是指请求的主体内容部分,包含了请求的所有操作内容。不同的请求类型,其请求体部分的结构是不同的,下面我们以会话创建、获取节点数据和更新节点数据这三个典型的请求体为例来对请求体进行详细分析。

ConnectRequest:会话创建

        ZooKeeper客户端和服务器在创建会话的时候,会发送ConnectRequest请求,该请求体中包含了数据节点的节点路径path和是否注册Watcher的标识watch,其数据结构定义如下:



SetDataRequest:更新节点数据

         ZooKeeper客户端在向服务器发送更新节点数据请求的时候,会发送SetDataRequest请求,该请求体中包含了数据节点的节点路径path、数据内容data和节点数据的期望版本号version,其数据结构定义如下:



        以上介绍了常见的三种典型请求体定义,针对不同的请求类型,ZooKeeper都会定义不同的请求体。

请求协议实例:获取节点数据

        上面我们分别介绍了请求头和请求体的协议定义,现在我们通过一个客户端“获取节点数据”的具体例子来进一步了解请求协议。

/**

 * 发起一次简单的获取节点数据请求

 *

 */

public class A_simple_get_data_request implements Watcher {

    public static void main(String[] args) throws Exception {

        ZooKeeper zk = new ZooKeeper("127.0.0.1:2181", 5000, new A_simple_get_data_request());

        zk.getData("/&7_2_4/get_data", true, null);

    }

    

    public void process(WatchedEvent arg0) {

    }

}

        上面是一个发起一次简单的获取节点数据内容请求的样例程序。客户端调用getData接口,实际上就是向ZooKeeper服务端发送了一个GetDataRequest请求。使用Wireshark获取到其发送的网络TCP包,如下图所示。



        在上图中,我们获取到了ZooKeeper客户端请求发出后,在TCP层数据传输的十六进制表示,其中带下划线的部分就是对应的GetDataRequest请求,即“[00,00,00,1d,00,00,00,01,00,00,00,04,00,00,00,10,2f,24,37,5f,32,5f,34,2f,67,65,74,5f,64,61,74,61,01]”。

十六进制位协议部分数值或字符串
00,00,00,1d0~3位是len,代表整个请求的数据包长度29
00,00,00,014~7位是xid,代表客户端请求的发起序号1
00,00,00,048~11位是type,代表客户端请求类型4(代表OpCode.getData)
00,00,00,1012~15位是len,代表节点路径的长度16(代表节点路径长度转换成十六进制是16位)
2f,24,37,5f,

32,5f,34,2f,

67,65,74,5f,

64,61,74,61
16~31位是path,代表节点路径/&7_2_4/get_data(通过比对ASCII码表转换成十进制即可)
0132位是watch,代表是否注册Watcher1(代表注册Watcher)

协议解析:响应部分

        上面我们已经对ZooKeeper请求部分的协议进行了解析,接下来我们看看服务器端响应的协议解析。我们首先来看响应协议的详细设计,下图定义了一个“获取节点数据”响应的完整协议定义。



响应头:ReplyHeader

        响应头中包含了每一个响应最基本的信息,包括xid、zxid和err:



        xid和上文中提到的请求头中的xid是一致的,响应中只是将请求中的xid原值返回。zxid代表ZooKeeper服务器上当前最新的事务ID。err则是一个错误码,当请求处理过程中出现异常情况时,会在这个错误码中标识出来,常见的包括处理成功(Code.OK:0)、节点不存在(Code.NONODE:101)和没有权限(Code.NOAUTH:102)等,所有这些错误码都被定义在类org.apahce.zookeeper.KeeperException.Code中。

响应体:Response

        协议的响应体部分是指响应的主体内容部分,包含了响应的所有返回数据。不同的响应类型,其响应体部分的结构是不同的,下面我们以会话创建、获取节点数据和更新节点数据这三个典型的响应体为例来对响应体进行详细分析。

ConnectResponse:会话创建

        针对客户端的会话创建请求,服务端会返回客户端一个ConnectResponse响应,该响应体中包含了协议的版本号protocolVersion、会话的超时时间timeOut、会话标识sessionId和会话密码passwd,其数据结构定义如下:



GetDataResponse:获取节点数据

        针对客户端的获取节点数据请求,服务端会返回客户端一个GetDataResponse响应,该响应体中包含了数据节点的数据内容data和节点状态stat,其数据结构定义如下:



SetDataResponse:更新节点数据

        针对客户端的更新节点数据请求,服务端会返回客户端一个SetDataResponse响应,该响应体中包含了最新的节点状态stat,其数据结构定义如下:



        以上介绍了常见的三种典型响应体定义,针对不同的响应类型,ZooKeeper都会定义不同的响应体。

响应协议示例:获取节点数据

        在上面的内容中,我们分别介绍了响应体和响应体的协议定义,现在我们再次通过上文中提到的客户端“获取节点数据”的例子来对响应协议做一个实际分析。

        这里的测试用例使用上面的示例程序(即A_simple_get_data_request类),只是这次我们使用Wireshark获取到服务端响应客户端时的网络TCP包,如下图所示。



         在上图中,我们获取到了ZooKeeper服务端响应发出之后,在TCP层数据传输的十六进制表示,其中带下划线的部分就是对应的GetDataResponse响应,即“[00,00,00,63,00,00,00,05,00,00,00,00,00,00,00,04,00,00,00,00,00,00,00,0b,69,27,6d,5f,63,6f,6e,74,65,6e,74,00,00,00,00,00,00,00,04,00,00,00,00,00,00,00,04,00,00,01,43,67,bd,0e,08,00,00,01,43,67,bd,0e,08,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,0b,00,00,00,00,00,00,00,00,00,00,00,04]”。

十六进制位协议部分数值或字符串
00,00,00,630~3位是len,代表整个响应的数据包长度99
00,00,00,054~7位是xid,代表客户端请求发起的序号5(代表本次请求是客户端会话创建后的第5次请求发送)
00,00,00,00,

00,00,00,04
8~165位是zxid,代表当前服务端处理过的最新的ZXID值4
00,00,00,0016~19位是err,代表错误码0(代表Code.OK)
00,00,00,0b20~23位是len,代表节点数据内容的长度11(代表接下去11位是数据内容的字节数组)
69,27,6d,5f,

63,6f,6e,74,

65,6e,74
24~34位是data,代表节点的数据内容i'm_content
00,00,00,00,

00,00,00,04
35~42位是czxid,代表创建该数据节点时的ZXID4
00,00,00,00,

00,00,00,04
43~50位是mzxid,代表最后一次修改该数据节点时的ZXID4
00,00,01,43,

67,bd,0e,08
51~58位是ctime,代表数据节点的创建时间13890148779752(即:2014-01-06 21:27:59)
00,00,01,43,

67,bd,0e,08
59~66位是mtime,代表数据节点最后一次变更的时间13890148779752(即:2014-01-06 21:27:59)
00,00,00,0067~70位是version,代表数据节点的内容的版本号0
00,00,00,0071~74位是cversion,代表数据节点的子节点版本号0
00,00,00,0075~78位是aversion,代表数据节点的ACL版本号0
00,00,00,00,

00,00,00,00
79~86位是ephemeralOwner,如果该数据节点是临时节点,那么就记录创建该临时节点的会话ID,如果是持久节点,则为00(代表该节点是持久节点)
00,00,00,0b87~90位是numChildren,代表数据节点的数据内容长度11
00,00,00,0091~94位是numChildren,代表数据节点的子节点个数0
00,00,00,00,

00,00,00,04
95~102位是pzxid,代表最后依次对子节点列表变更的PZXID4
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: