Zookeeper学习之Jute序列化以及通信协议详解
一、Jute序列化工具
1、Jute概述
Zookeeper的客戶端與服務(wù)端之間會進行一系列的網(wǎng)絡(luò)通信來實現(xiàn)數(shù)據(jù)傳輸,Zookeeper使用Jute組件來完成數(shù)據(jù)的序列化和反序列化操作,其用于Zookeeper進行網(wǎng)絡(luò)數(shù)據(jù)傳輸和本地磁盤數(shù)據(jù)存儲的序列化和反序列化工作。
實體類要使用Jute進行序列化和反序列化步驟:
1.需要實現(xiàn)Record接口的serialize和deserialize方法;
2.構(gòu)建一個序列化器BinaryOutputArchive;
3.序列化:調(diào)用實體類的serialize方法,將對象序列化到指定的tag中去,比如這里將對象序列化到header中;
4.反序列化:調(diào)用實體類的deserialize方法,從指定的tag中反序列化出數(shù)據(jù)內(nèi)容。
2、Record接口
Zookeeper中所需要進行網(wǎng)絡(luò)傳輸或是本地磁盤存儲的類型定義,都實現(xiàn)了該接口,是Jute序列化的核心。Record定義了兩個基本的方法,分別是serialize和deserialize,分別用于序列化和反序列化。其中archive是底層真正的序列化器和反序列化器,并且每個archive中可以包含對多個對象的序列化和反序列化,因此兩個接口中都標記了參數(shù)tag,用于序列化器和反序列化器標識對象自己的標記。
3、OutputArchive和InputArchive
OutputArchive和InputArchive分別是Jute底層的序列化器和反序列化器定義。有BinaryOutputArchive/BinaryInputArchive、CsvOutputArchive/CsvInputArchive和XmlOutputArchive/XmlInputArchive三種實現(xiàn),無論哪種實現(xiàn)都是基于OutputStream和InputStream進行操作。
BinaryOutputArchive對數(shù)據(jù)對象的序列化和反序列化,主要用于進行網(wǎng)絡(luò)傳輸和本地磁盤的存儲,是Zookeeper底層最主要的序列化方式。CsvOutputArchive對數(shù)據(jù)的序列化,更多的是方便數(shù)據(jù)的可視化展示,因此被用在toString方法中。XmlOutputArchive則是為了將數(shù)據(jù)對象以xml格式保存和還原,但目前在Zookeeper中基本沒使用到。
4、測試示例
首先我們構(gòu)建一個實體類,實現(xiàn)Record接口的serialize和deserialize方法:
public class MockReHeader implements Record {
private long sessionId;
private String type;
public MockReHeader() {}
public MockReHeader(long sessionId, String type) {
this.sessionId = sessionId;
this.type = type;
}
public void setSessionId(long sessionId) {
this.sessionId = sessionId;
}
public void setType(String type) {
this.type = type;
}
public long getSessionId() {
return sessionId;
}
public String getType() {
return type;
}
public void serialize(OutputArchive outputArchive, String tag) throws java.io.IOException {
outputArchive.startRecord(this, tag);
outputArchive.writeLong(sessionId, "sessionId");
outputArchive.writeString(type, "type");
outputArchive.endRecord(this, tag);
}
public void deserialize(InputArchive inputArchive, String tag) throws java.io.IOException {
inputArchive.startRecord(tag);
this.sessionId = inputArchive.readLong("sessionId");
this.type = inputArchive.readString("type");
inputArchive.endRecord(tag);
}
@Override
public String toString() {
return "sessionId = " + sessionId + ", type = " + type;
}
}
可以看到MockReHeader實體類需要實現(xiàn)Record接口并且實現(xiàn)serialize和deserialize方法。OutputArchive和InputArchive分別是Jute底層的序列化器和反序列化器。
接下來測試我們的實體類:
public class JuteTest {
public static void main(String[] args) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
BinaryOutputArchive binaryOutputArchive = BinaryOutputArchive.getArchive(byteArrayOutputStream);
new MockReHeader(0x3421eccb92a34el, "ping").serialize(binaryOutputArchive, "header");
ByteBuffer byteBuffer = ByteBuffer.wrap(byteArrayOutputStream.toByteArray());
ByteBufferInputStream byteBufferInputStream = new ByteBufferInputStream(byteBuffer);
BinaryInputArchive binaryInputArchive = BinaryInputArchive.getArchive(byteBufferInputStream);
MockReHeader mockReHeader = new MockReHeader();
System.out.println(mockReHeader);
mockReHeader.deserialize(binaryInputArchive, "header");
System.out.println(mockReHeader);
byteBufferInputStream.close();
byteArrayOutputStream.close();
}
}
輸出如下:
sessionId = 0, type = null sessionId = 14673999700337486, type = ping
5、zookeeper.jute
在Zookeeper的src文件夾下有zookeeper.jute文件,這個文件定義了所有的實體類的所屬包名、類名及類的所有成員變量和類型,該文件會在源代碼編譯時,Jute會使用不同的代碼生成器為這些類定義生成實際編程語言的類文件,如java語言生成的類文件保存在src/java/generated目錄下,每個類都會實現(xiàn)Record接口。zookeeper.jute文件部分源碼如下:
module org.apache.zookeeper.data {
class Id {
ustring scheme;
ustring id;
}
class ACL {
int perms;
Id id;
}
// information shared with the client
class Stat {
long czxid; // created zxid
long mzxid; // last modified zxid
long ctime; // created
long mtime; // last modified
int version; // version
int cversion; // child version
int aversion; // acl version
long ephemeralOwner; // owner id if ephemeral, 0 otw
int dataLength; //length of the data in the node
int numChildren; //number of children of this node
long pzxid; // last modified children
}
// information explicitly stored by the server persistently
class StatPersisted {
long czxid; // created zxid
long mzxid; // last modified zxid
long ctime; // created
long mtime; // last modified
int version; // version
int cversion; // child version
int aversion; // acl version
long ephemeralOwner; // owner id if ephemeral, 0 otw
long pzxid; // last modified children
}
}
module org.apache.zookeeper.proto {
class ConnectRequest {
int protocolVersion;
long lastZxidSeen;
int timeOut;
long sessionId;
buffer passwd;
}
class ConnectResponse {
int protocolVersion;
int timeOut;
long sessionId;
buffer passwd;
}
class SetWatches {
long relativeZxid;
vector<ustring>dataWatches;
vector<ustring>existWatches;
vector<ustring>childWatches;
}class GetDataRequest {
ustring path;
boolean watch;
}
class SetDataRequest {
ustring path;
buffer data;
int version;
}
class ReconfigRequest {
ustring joiningServers;
ustring leavingServers;
ustring newMembers;
long curConfigId;
}
}
module org.apache.zookeeper.server.quorum {
class LearnerInfo {
long serverid;
int protocolVersion;
long configVersion;
}
class QuorumPacket {
int type; // Request, Ack, Commit, Ping
long zxid;
buffer data; // Only significant when type is request
vector<org.apache.zookeeper.data.Id> authinfo;
}
class QuorumAuthPacket {
long magic;
int status;
buffer token;
}
}
module org.apache.zookeeper.server.persistence {
class FileHeader {
int magic;
int version;
long dbid;
}
}
二、ZooKeeper通信協(xié)議
基于TCP/IP協(xié)議,Zookeeper實現(xiàn)了自己的通信協(xié)議來玩按成客戶端與服務(wù)端、服務(wù)端與服務(wù)端之間的網(wǎng)絡(luò)通信,對于請求,主要包含請求頭和請求體,對于響應(yīng),主要包含響應(yīng)頭和響應(yīng)體。
1、請求協(xié)議
對于請求協(xié)議而言,如下為獲取節(jié)點數(shù)據(jù)請求的完整協(xié)議定義:
class RequestHeader {
int xid;
int type;
}
從zookeeper.jute中可知RequestHeader包含了xid和type,xid用于記錄客戶端請求發(fā)起的先后序號,用來確保單個客戶端請求的響應(yīng)順序,type代表請求的操作類型,如創(chuàng)建節(jié)點(OpCode.create)、刪除節(jié)點(OpCode.delete)、獲取節(jié)點數(shù)據(jù)(OpCode.getData)。
協(xié)議的請求主體內(nèi)容部分,包含了請求的所有操作內(nèi)容,不同的請求類型請求體不同。對于會話創(chuàng)建而言,其請求體如下:
class ConnectRequest {
int protocolVersion;
long lastZxidSeen;
int timeOut;
long sessionId;
buffer passwd;
}
Zookeeper客戶端和服務(wù)器在創(chuàng)建會話時,會發(fā)送ConnectRequest請求,該請求包含協(xié)議版本號protocolVersion、最近一次接收到服務(wù)器ZXID lastZxidSeen、會話超時時間timeOut、會話標識sessionId和會話密碼passwd。
對于獲取節(jié)點數(shù)據(jù)而言,其請求體如下:
class GetDataRequest {
ustring path;
boolean watch;
}
Zookeeper客戶端在向服務(wù)器發(fā)送節(jié)點數(shù)據(jù)請求時,會發(fā)送GetDataRequest請求,該請求包含了數(shù)據(jù)節(jié)點路徑path、是否注冊Watcher的標識watch。
對于更新節(jié)點數(shù)據(jù)而言,其請求體如下:
class SetDataRequest {
ustring path;
buffer data;
int version;
}
Zookeeper客戶端在向服務(wù)器發(fā)送更新節(jié)點數(shù)據(jù)請求時,會發(fā)送SetDataRequest請求,該請求包含了數(shù)據(jù)節(jié)點路徑path、數(shù)據(jù)內(nèi)容data、節(jié)點數(shù)據(jù)的期望版本號version。
針對不同的請求類型,Zookeeper都會定義不同的請求體,可以在zookeeper.jute中查看,所有的請求都會按照此文件的描述進行序列化/反序列化。
2、響應(yīng)協(xié)議
對于響應(yīng)協(xié)議而言,如下為獲取節(jié)點數(shù)據(jù)響應(yīng)的完整協(xié)議定義:
響應(yīng)頭中包含了每個響應(yīng)最基本的信息,包括xid、zxid和err:
class ReplyHeader {
int xid;
long zxid;
int err;
}
xid與請求頭中的xid一致,zxid表示Zookeeper服務(wù)器上當前最新的事務(wù)ID,err則是一個錯誤碼,表示當請求處理過程出現(xiàn)異常情況時,就會在錯誤碼中標識出來,常見的包括處理成功(Code.OK)、節(jié)點不存在(Code.NONODE)、沒有權(quán)限(Code.NOAUTH)。
協(xié)議的響應(yīng)主體內(nèi)容部分,包含了響應(yīng)的所有數(shù)據(jù),不同的響應(yīng)類型請求體不同。對于會話創(chuàng)建而言,其響應(yīng)體如下:
class ConnectResponse {
int protocolVersion;
int timeOut;
long sessionId;
buffer passwd;
}
針對客戶端的會話創(chuàng)建請求,服務(wù)端會返回客戶端一個ConnectResponse響應(yīng),該響應(yīng)體包含了版本號protocolVersion、會話的超時時間timeOut、會話標識sessionId和會話密碼passwd。
對于獲取節(jié)點數(shù)據(jù)而言,其響應(yīng)體如下:
class GetDataResponse {
buffer data;
org.apache.zookeeper.data.Stat stat;
}
針對客戶端的獲取節(jié)點數(shù)據(jù)請求,服務(wù)端會返回客戶端一個GetDataResponse響應(yīng),該響應(yīng)體包含了數(shù)據(jù)節(jié)點內(nèi)容data、節(jié)點狀態(tài)stat。
對于更新節(jié)點數(shù)據(jù)而言,其響應(yīng)體如下:
class SetDataResponse {
org.apache.zookeeper.data.Stat stat;
}
針對客戶端的更新節(jié)點數(shù)據(jù)請求,服務(wù)端會返回客戶端一個SetDataResponse響應(yīng),該響應(yīng)體包含了最新的節(jié)點狀態(tài)stat。
針對不同的響應(yīng)類型,Zookeeper都會定義不同的響應(yīng)體,也可以在zookeeper.jute中查看。
總結(jié)
以上是生活随笔為你收集整理的Zookeeper学习之Jute序列化以及通信协议详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 写一本书作者到底能拿到多少稿酬?
- 下一篇: stp理论