【分布式】Zookeeper源码分析:Jute序列化
概要
Zookeeper的客户端和服务端进行网络通信实现数据传输使用了序列化组件Jute
,它最初是Hadoop中默认的序列化组件(Record IO)中的序列化组件,后来Hadoop从0.21.0版本开始废弃了Record IO,而使用Avro这个序列化框架,而Zookeeper官方由于一些历史原因依然使用了Jute这个古老的序列化组件,它对数据的序列化和反序列化操作是zookeeper高效传输数据的基础。本文对Jute在网络通信底层的一些关键技术点进行分析,主要内容如下:
第1部分 Jute介绍
Jute部分主要在org.apache.jute
包中
Jute主要用于Zookeeper进行网络传输和本地磁盘数据的序列化及反序列化工作。首先附上整体框架:
Record接口
Jute中定义了自己独特的序列化格式Record
,Zookeeper中所有需要进行网络传输或者本地磁盘存储的类型定义都实现了该接口。该接口中有两个方法:serialize
和deserialize
,所有继承它的类通过这两个方法定义序列化和反序列化的方式,其中OutputArchive
和InputArchive
是真正的序列化器和反序列化器。
public interface Record {
public void serialize(OutputArchive archive, String tag)
throws IOException;
public void deserialize(InputArchive archive, String tag)
throws IOException;
}
注意,这两个方法中都有参数tag
,这是因为每个Archive
可以包含对多个对象的序列化和反序列化,这两个接口可以用于标识对象。以RequestHeader
为例:
public void serialize(OutputArchive a_, String tag) throws java.io.IOException {
a_.startRecord(this,tag);
a_.writeInt(xid,"xid");
a_.writeInt(type,"type");
a_.endRecord(this,tag);
}
public void deserialize(InputArchive a_, String tag) throws java.io.IOException {
a_.startRecord(tag);
xid=a_.readInt("xid");
type=a_.readInt("type");
a_.endRecord(tag);
}
RequestHeader
中包含了xid
和type
两个属性,序列化/反序列化遵循三个步骤:
- startRecord
- read/writeXXX
- endRecord
OutputArchive和InputArchive接口
序列化和反序列化接口的定义部分,在Zookeeper中分别有BinaryOutputArchive/BinaryInputArchive
、CsvOutputArchive\CsvInputArchive
、XmlOutputArchive\XmlInputArchive
三种实现,基于Binary是Zookeeper中最主要的序列化方式。
Index接口
用于vector
或map
中指示器,如:
Index idx = startVector(...);// 开始读取vector
while (!idx.done()) { // 判断是否读/写完
// read element of a vector
idx.incr(); //index自增,即读/写下一个
}
第2部分 通信协议
Zookeeper在TCP/IP
基础上实现了自己的通信协议,主要在 org.apache.zookeeper.proto
包中。
- 请求:包含请求头(RequestHeader)和请求体(XXXRequest)
- 响应:包含响应头(Response)和响应体(XXXResponse)
请求协议
以下是一个获取节点数据请求的完整协议定义:
协议内容解析:
RequestHeader请求头
包含xid和type,xid用户记录客户端发起的先后序号,确保单个客户端请求的响应顺序;type代表请求的操作类型,包括OpCode.create
、OpCode.delete
、OpCode.getData
、OpCode.getChildren
等。根据协议规定,除了会话创建
的请求,所有的请求必须包含请求头。
XXXRequest请求体
请求体根据请求类型的不同,Zookeeper中有不同的请求体实现。
- ConnectRequest
Zookeeper在创建会话时,请求体中包含了协议的版本号、最近一次接收到的服务器的Zxid,会话超时时间、会话id和会话密码,如果创建的会话包含会话id和密码则认为客户端正在进行会话重连。
- SetDataRequest
客户端在发送更新节点数据时会发送该请求,请求体中包含数据节点路径、数据内容data和节点数据期望的版本号。
- CreateRequest
客户端新建节点时会发送该请求,请求体中包含节点路径,数据内容,acl认证内容和节点类型。
在Zookeeper中,节点类型包含四种:
PERSISTENT (0, false, false), // 永久节点
PERSISTENT_SEQUENTIAL (2, false, true), //永久顺序节点
EPHEMERAL (1, true, false), //临时节点
EPHEMERAL_SEQUENTIAL (3, true, true); //临时顺序节点
响应协议
以下是一个获取数据节点的响应的完整协议:
ReplyHeader响应头
包含每一个响应最基本的信息,包括客户端发起的先后序号xid,Zookeeper服务器上最新的事务ID zxid,可以根据返回错误码err判断请求响应情况,如处理成功(Code.OK: 0)、节点不存在(Code.NONODE: 101)、没有权限(Code.NOAUTH: 102)。
XXXReponse响应体
响应体是响应的主体部分,不同的响应类型具有不同的响应体。
- ConnectResponse
针对客户端的会话创建请求,服务端会返回客户端一个ConnectResponse响应,该响应体包含了版本号protocolVersion、会话的超时时间timeOut、会话标识sessionId和会话密码passwd。
- SetDataResponse
针对客户端的更新节点数据请求,服务端会返回客户端一个SetDataResponse响应,该响应体包含了最新的节点状态stat。
- CreateResponse
针对客户端的创建节点请求,服务端会反回客户端一个CreateResponse响应,该响应体包含了创建节点的路径。
第3部分 总结
本文介绍了Jute序列化组件及客户端与服务端、服务端与服务端的通信协议,比较简单,但是它是Zookeeper进行分布式通信的基础,值得一看。