【分布式】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中所有需要进行网络传输或者本地磁盘存储的类型定义都实现了该接口。该接口中有两个方法:serializedeserialize,所有继承它的类通过这两个方法定义序列化和反序列化的方式,其中OutputArchiveInputArchive是真正的序列化器和反序列化器。

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中包含了xidtype两个属性,序列化/反序列化遵循三个步骤:

- startRecord
- read/writeXXX
- endRecord

OutputArchive和InputArchive接口

序列化和反序列化接口的定义部分,在Zookeeper中分别有BinaryOutputArchive/BinaryInputArchiveCsvOutputArchive\CsvInputArchiveXmlOutputArchive\XmlInputArchive三种实现,基于Binary是Zookeeper中最主要的序列化方式。

Index接口

用于vectormap中指示器,如:

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.createOpCode.deleteOpCode.getDataOpCode.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进行分布式通信的基础,值得一看。

posted @ 2020-09-03 21:35  fancyfairy  阅读(376)  评论(0)    收藏  举报
翻到最前~