Zookeeper通信协议
基于TCP/IP协议,ZooKeeper实现了自己的通信协议来完成客户端与服务端、服务端与服务端之间的网络通信。ZooKeeper通信协议整体.上的设计非常简单,对于请求,主要.包含请求头和请求体,而对于响应,则主要包含响应头和响应体,如下图:
协议解析:请求部分
下图是一个"获取结点数据"请求的完整协议定义:
请求同:RequestHeader
请求头中包含了请求的最基本的信息,包括xid和type:
xid用于记录客户端请求发起的先后序号,用来确保单个客户端请求的响应顺序。
type代表请求的操作类型,常见的包括
- 创建节点(OpCode.create: 1)
- 删除节点(OpCode.create: 2)
- 获取节点数据(OpCode.getData: 4)
所有这些操作类型都被定义在类org.apache.zookeeper.ZooDefs.0pCode中
。根据协议规定,除非是“会话创建”请求,其他所有的客户端请求中都会带上请求头。
请求体:Request
协议的请求体部分是指请求的主体内容部分,包含了请求的所有操作内容。不同的请求类型,其请求体部分的结构是不同的,下面以会话创建、获取节点数据和更新节点数据这三个典型的请求体为例来对请求体进行详细分析。
ConnectRequest:会话创建
ZooKeeper客户端和服务器在创建会话的时候,会发送ConnectRequest请求,该请求体中包含了协议的版本号protocolVersion、 最近一次接收到的服务器ZXID lastZxidSeen、会话超时时间time0ut、会话标识sessionId和会话密码passwd
GetDataRequest:获取结点数据
ZooKeeper客户端在向服务器发送获取节点数据请求的时候,会发送GetDataRequest请求,该请求体中包含了数据节点的节点路径path和是否注册Watcher的标识watch
,其数据结构定义如下:
SetDataRequest:更新结点数据
ZooKeeper客户端在向服务器发送更新节点数据请求的时候,会发送SetDataRequest请求,该请求体中包含了数据节点的节点路径path、数据内容data和节点数据的期望版本号version
,其数据结构定义如下:
请求协议实例:获取结点数据
发起一次简单的获取节点数据请求:
public class A_simple_get_data_request implements Watcher {
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
ZooKeeper zk = new ZooKeeper("domain1.book.zookeeper",
5000,
new A_simple_get_data_request());
zk.getData("/$7_2_4/get_data", true, null);
}
@Override
public void process(WatchedEvent event) {
}
}
实际上就是向Zookeeper服务端发送了一个GetDataRequest请求。使用Wireshark获取到其发送的网络TCP包:
我们获取到了ZooKeeper 客户端请求发出后,在TCP层数据传输的十六进制表示,其中带下划线的部分就是对应的GetDataRequest请求,即“[00,00,00,ld,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]”
。通过比对下图中的GetDataRequest请求的完整协议定义,我们来分析下这个十六进制字节数组的含义:
解析协议:响应部分
GetDataResponse响应完整协议定义:
响应头:ReplyHeader
响应头中包含了每- -个响应最基本的信息,包括xid、zxid 和err:
xid和上文中提到的请求头中的xid是一致的,响应中只是将请求中的xid原值返回。
zxid代表ZooKeeper服务器上当前最新的事务ID。
err则是一个错误码,当请求处理过程中出现异常情况时,会在这个错误码中标识出来,常见的包括
- 处理成功(Code.0K:0)
- 节点不存在(Code. NONODE: 101)
- 没有权限(Code. NOAUTH: 102) 等
所有这些错误码都被定义在类org.apache.zookeeper.KeeperException. Code
中。
响应体:Response
协议的响应体部分是指响应的主体内容部分,包含了响应的所有返回数据。不同的响应类型,其响应体部分的结构是不同的,下面我们以会话创建、获取节点数据和更新节点数据这三个典型的响应体为例来对响应体进行详细分析。
ConnectResponse:会话创建
针对客户端的会话创建请求,服务端会返回客户端一-个ConnectResponse响应,该响应体中包含了协议的版本号protocolVersion、会话的超时时间t imeOut、会话标识sessionId和会话密码passwd,其数据结构定义如下:
GetDataResponse:获取结点数据
针对客户端的获取节点数据请求,服务端会返回客户端一个GetDataResponse响应,该响应体中包含了数据节点的数据内容data和节点状态stat,其数据结构定义如下:
SetDataResponse:更新节点数据
针对客户端的更新节点数据请求,服务端会返回客户端-一个SetDataResponse响应,该响应体中包含了最新的节点状态stat,其数据结构定义如下:
响应协议实例:获取结点数据
依然使用上面的示例程序,这次使用Wireshark后去到服务端响应客户端时的网络TCP包:
在图7-16中,我们获取到了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]”
。通过比对下图中的GetDataRes ponse响应完整协议定义,我们来分析下这个十六进制字节数组的含义: