其實很多時候我們都在使用zkclient這款jar包對zk進行相關的操作,但是在zkclient裏面到底發生了什麼,我們卻並不是很清楚。對zk的瞭解出了簡單的節點創建,刪除,監聽以外,我們還可以加深對它的思想理解。
下邊我們來深入探討一下zk的內部機制:
其實我們清楚一點,zk是採用了java語言進行編寫的,因此關於zk這部分的內容對於java程序員來說是比較好接受的。
現在讓我們來看下zk裏面序列化部分:
在jute裏面有一個叫做record的接口,專門用於定義序列化和反序列化操作:
package org.apache.jute;
import java.io.IOException;
/**
* Interface that is implemented by generated classes.
*
*/
public interface Record {
public void serialize(OutputArchive archive, String tag)
throws IOException;
public void deserialize(InputArchive archive, String tag)
throws IOException;
}
zk內部的jute是自己研發的一款用於處理序列化和反序列化操作的工具。核心的序列化和反序列化器有以下幾種:
BinaryInputArchive 處理二進制數據
BinaryOutputArchive
CsvInputArchive 處理csv格式數據
CsvOutputArchive
XmlInputArchive處理xml格式數據
XmlOutputArchive
下邊我們結合一段代碼案例來深入瞭解jute的序列化器工作使用方式:
package 源碼分析.jute包;
import lombok.Data;
import org.apache.jute.InputArchive;
import org.apache.jute.OutputArchive;
import org.apache.jute.Record;
import java.io.IOException;
/**
* @author idea
* @data 2019/10/27
*/
@Data
public class TestBean implements Record {
private int number;
private String name;
public TestBean(int number, String name) {
this.number = number;
this.name = name;
}
public TestBean(){}
@Override
public void serialize(OutputArchive archive, String tag) throws IOException {
System.out.println("tag output:"+tag);
archive.startRecord(this,tag);
archive.writeInt(this.number,"number");
archive.writeString(this.name,"name");
archive.endRecord(this,tag);
}
@Override
public void deserialize(InputArchive archive, String tag) throws IOException {
System.out.println("tag:"+tag);
archive.startRecord(tag);
this.number=archive.readInt("number");
this.name=archive.readString("name");
archive.endRecord(tag);
}
}
接下來是我們的測試類
package 源碼分析.jute包;
import org.apache.jute.BinaryInputArchive;
import org.apache.jute.BinaryOutputArchive;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
/**
* @author idea
* @data 2019/10/27
*/
public class TestDemo {
public static void main(String[] args) throws IOException {
//先序列化操作
ByteArrayOutputStream baos=new ByteArrayOutputStream();
BinaryOutputArchive boa= BinaryOutputArchive.getArchive(baos);
new TestBean(1,"idea").serialize(boa,"tag1");
byte[] array=baos.toByteArray();
ByteArrayInputStream bins=new ByteArrayInputStream(array);
BinaryInputArchive bia=BinaryInputArchive.getArchive(bins);
TestBean testBean=new TestBean();
testBean.deserialize(bia,"tag1");
System.out.println("int="+testBean.getNumber()+"--string="+testBean.getName());
bins.close();
baos.close();
}
}
處理完畢之後,便是我們的數據展示環節了。
看完之後你可能會覺得,怎麼zk裏面的數據類型進行序列化和反序列化需要提前對bean做如此複雜的操作啊?是的,在zk源碼的
包org.apache.zookeeper.data下邊,會看到相應的類都做了非常複雜的操作,舉個案例來說,例如說Stat這個對象,在它的源碼裏面可以看到這樣的內容描述:
// File generated by hadoop record compiler. Do not edit.
package org.apache.zookeeper.data;
import org.apache.jute.*;
public class Stat implements Record {
private long czxid;
private long mzxid;
private long ctime;
private long mtime;
private int version;
private int cversion;
private int aversion;
private long ephemeralOwner;
private int dataLength;
private int numChildren;
private long pzxid;
public Stat() {
}
public Stat(
long czxid,
long mzxid,
long ctime,
long mtime,
int version,
int cversion,
int aversion,
long ephemeralOwner,
int dataLength,
int numChildren,
long pzxid) {
this.czxid=czxid;
this.mzxid=mzxid;
this.ctime=ctime;
this.mtime=mtime;
this.version=version;
this.cversion=cversion;
this.aversion=aversion;
this.ephemeralOwner=ephemeralOwner;
this.dataLength=dataLength;
this.numChildren=numChildren;
this.pzxid=pzxid;
}
public long getCzxid() {
return czxid;
}
public void setCzxid(long m_) {
czxid=m_;
}
public long getMzxid() {
return mzxid;
}
public void setMzxid(long m_) {
mzxid=m_;
}
public long getCtime() {
return ctime;
}
public void setCtime(long m_) {
ctime=m_;
}
public long getMtime() {
return mtime;
}
public void setMtime(long m_) {
mtime=m_;
}
public int getVersion() {
return version;
}
public void setVersion(int m_) {
version=m_;
}
public int getCversion() {
return cversion;
}
public void setCversion(int m_) {
cversion=m_;
}
public int getAversion() {
return aversion;
}
public void setAversion(int m_) {
aversion=m_;
}
public long getEphemeralOwner() {
return ephemeralOwner;
}
public void setEphemeralOwner(long m_) {
ephemeralOwner=m_;
}
public int getDataLength() {
return dataLength;
}
public void setDataLength(int m_) {
dataLength=m_;
}
public int getNumChildren() {
return numChildren;
}
public void setNumChildren(int m_) {
numChildren=m_;
}
public long getPzxid() {
return pzxid;
}
public void setPzxid(long m_) {
pzxid=m_;
}
public void serialize(OutputArchive a_, String tag) throws java.io.IOException {
a_.startRecord(this,tag);
a_.writeLong(czxid,"czxid");
a_.writeLong(mzxid,"mzxid");
a_.writeLong(ctime,"ctime");
a_.writeLong(mtime,"mtime");
a_.writeInt(version,"version");
a_.writeInt(cversion,"cversion");
a_.writeInt(aversion,"aversion");
a_.writeLong(ephemeralOwner,"ephemeralOwner");
a_.writeInt(dataLength,"dataLength");
a_.writeInt(numChildren,"numChildren");
a_.writeLong(pzxid,"pzxid");
a_.endRecord(this,tag);
}
public void deserialize(InputArchive a_, String tag) throws java.io.IOException {
a_.startRecord(tag);
czxid=a_.readLong("czxid");
mzxid=a_.readLong("mzxid");
ctime=a_.readLong("ctime");
mtime=a_.readLong("mtime");
version=a_.readInt("version");
cversion=a_.readInt("cversion");
aversion=a_.readInt("aversion");
ephemeralOwner=a_.readLong("ephemeralOwner");
dataLength=a_.readInt("dataLength");
numChildren=a_.readInt("numChildren");
pzxid=a_.readLong("pzxid");
a_.endRecord(tag);
}
public String toString() {
try {
java.io.ByteArrayOutputStream s =
new java.io.ByteArrayOutputStream();
CsvOutputArchive a_ =
new CsvOutputArchive(s);
a_.startRecord(this,"");
a_.writeLong(czxid,"czxid");
a_.writeLong(mzxid,"mzxid");
a_.writeLong(ctime,"ctime");
a_.writeLong(mtime,"mtime");
a_.writeInt(version,"version");
a_.writeInt(cversion,"cversion");
a_.writeInt(aversion,"aversion");
a_.writeLong(ephemeralOwner,"ephemeralOwner");
a_.writeInt(dataLength,"dataLength");
a_.writeInt(numChildren,"numChildren");
a_.writeLong(pzxid,"pzxid");
a_.endRecord(this,"");
return new String(s.toByteArray(), "UTF-8");
} catch (Throwable ex) {
ex.printStackTrace();
}
return "ERROR";
}
public void write(java.io.DataOutput out) throws java.io.IOException {
BinaryOutputArchive archive = new BinaryOutputArchive(out);
serialize(archive, "");
}
public void readFields(java.io.DataInput in) throws java.io.IOException {
BinaryInputArchive archive = new BinaryInputArchive(in);
deserialize(archive, "");
}
public int compareTo (Object peer_) throws ClassCastException {
if (!(peer_ instanceof Stat)) {
throw new ClassCastException("Comparing different types of records.");
}
Stat peer = (Stat) peer_;
int ret = 0;
ret = (czxid == peer.czxid)? 0 :((czxid<peer.czxid)?-1:1);
if (ret != 0) return ret;
ret = (mzxid == peer.mzxid)? 0 :((mzxid<peer.mzxid)?-1:1);
if (ret != 0) return ret;
ret = (ctime == peer.ctime)? 0 :((ctime<peer.ctime)?-1:1);
if (ret != 0) return ret;
ret = (mtime == peer.mtime)? 0 :((mtime<peer.mtime)?-1:1);
if (ret != 0) return ret;
ret = (version == peer.version)? 0 :((version<peer.version)?-1:1);
if (ret != 0) return ret;
ret = (cversion == peer.cversion)? 0 :((cversion<peer.cversion)?-1:1);
if (ret != 0) return ret;
ret = (aversion == peer.aversion)? 0 :((aversion<peer.aversion)?-1:1);
if (ret != 0) return ret;
ret = (ephemeralOwner == peer.ephemeralOwner)? 0 :((ephemeralOwner<peer.ephemeralOwner)?-1:1);
if (ret != 0) return ret;
ret = (dataLength == peer.dataLength)? 0 :((dataLength<peer.dataLength)?-1:1);
if (ret != 0) return ret;
ret = (numChildren == peer.numChildren)? 0 :((numChildren<peer.numChildren)?-1:1);
if (ret != 0) return ret;
ret = (pzxid == peer.pzxid)? 0 :((pzxid<peer.pzxid)?-1:1);
if (ret != 0) return ret;
return ret;
}
public boolean equals(Object peer_) {
if (!(peer_ instanceof Stat)) {
return false;
}
if (peer_ == this) {
return true;
}
Stat peer = (Stat) peer_;
boolean ret = false;
ret = (czxid==peer.czxid);
if (!ret) return ret;
ret = (mzxid==peer.mzxid);
if (!ret) return ret;
ret = (ctime==peer.ctime);
if (!ret) return ret;
ret = (mtime==peer.mtime);
if (!ret) return ret;
ret = (version==peer.version);
if (!ret) return ret;
ret = (cversion==peer.cversion);
if (!ret) return ret;
ret = (aversion==peer.aversion);
if (!ret) return ret;
ret = (ephemeralOwner==peer.ephemeralOwner);
if (!ret) return ret;
ret = (dataLength==peer.dataLength);
if (!ret) return ret;
ret = (numChildren==peer.numChildren);
if (!ret) return ret;
ret = (pzxid==peer.pzxid);
if (!ret) return ret;
return ret;
}
public int hashCode() {
int result = 17;
int ret;
ret = (int) (czxid^(czxid>>>32));
result = 37*result + ret;
ret = (int) (mzxid^(mzxid>>>32));
result = 37*result + ret;
ret = (int) (ctime^(ctime>>>32));
result = 37*result + ret;
ret = (int) (mtime^(mtime>>>32));
result = 37*result + ret;
ret = (int)version;
result = 37*result + ret;
ret = (int)cversion;
result = 37*result + ret;
ret = (int)aversion;
result = 37*result + ret;
ret = (int) (ephemeralOwner^(ephemeralOwner>>>32));
result = 37*result + ret;
ret = (int)dataLength;
result = 37*result + ret;
ret = (int)numChildren;
result = 37*result + ret;
ret = (int) (pzxid^(pzxid>>>32));
result = 37*result + ret;
return result;
}
public static String signature() {
return "LStat(lllliiiliil)";
}
}
這段代碼非常的長,通過一些晚上的性能比對測試,會發現在速度方便jute的序列化性能要比protobuf好一些,但是碼流方面後者更佳。
本文講解了關於zk內部的序列化機制,認真觀看之後會發現,zk並沒有對jdk本身的序列化方式做什麼優化的手段,因此並沒有什麼過多的特別之處。更多的就是在DataInputStream那個位置加入了一些數據的接受和處理。
讓我們回顧一下以前寫過的一篇序列化文章,或許兩篇文章對比之後你會有不同的收穫和體會。
https://blog.csdn.net/Danny_idea/article/details/99620144