zookeeper源码探究(一) ------zookeeper的序列化和通信协议

zookeeper的序列化解决方案

zookeeper采用了jute框架进行数据的序列化传输,选择jute并不是因为这个框架的序列化解决方式性能很好,相反,像apache Avro,thrift等框架的性能要优于它,选择他是因为新老版本的兼容问题
使用jute实现java序列化也很简单,只需要实现Record接口,重写他的serialize和deserialize方法就可以了, jute框架支持非常多的数据类型的转化,有整型,双精度数,长整型,字符串等等
一例胜千言 :

class TestJute implements Record throws ....{
    
    
	private long ids;
	private Strinng name;
	.....
	public void serialize(OutputArchive a, String tag){
    
    
		a.startRecord(this.tag);
		a.writeLong(ids, "ids");
		a.writeString(name, "name");
		......
		a.endRecord(this.tag);
	}
	public void deserialize(INputArchive a, String tag){
    
    
		.... 同理
	}
}

可以看的出Out / INputArchive这个类还是蛮关键的两个类,我们序列化的方法写在这两个类中,具体调用时,我们根据目的数据类型可以有不同的实现 : xml、binary、Csv

我们接着深入一些,首先从binary的序列化方法开始,了解一下jute的底层源码,我们先来拿上面的writeString方法开使分析

 void writeString(String s, String tag){
    
    
	if (s == null){
    
       // 如果字符串为空,则将其当作值为-1的字符串进行序列化
		writeInt(-1, "len");
		return;
	}
	// 如果不为空,调用下面的这个方法   看样子,这个方法便是BinaryOutPutArchive的核心方法了
	ByteBuffer bb = stringtoByteBuffer(s);	 
 }

stringToByteBuffer

	private ByteBuffer bb = ByteBuffer.allocate(1024);
	ByteBuffer stringToByteBuffer(CharSequence c){
    
    
		if( c < 0x80){
    
    
			bb.put((byte)c)    // 对于字母或者是数字,直接使用一个byte
		}else if(c < 0x800){
    
    
			bb.put((byte)(0xc0 | c >> 6)));
			bb.put((byte)(0x80 | (c & 0x3f)));
		}
	}

Binary二进制序列化方式的底层实现相对简单,只是采用将对应的java对象转化为二进制字节流的方式,Binary方式序列化的优点有很多,无论是windows操作系统和mac操作系统,其底层都是对二进制文件的编译与解析也是一样的,所有操作系统都能对二进制文件进行操作,缺点是,在不同的操作系统下,会产生大端小端的问题。
xml、Csv原理和Binary大同小异, 对于xml(extensible markup language)来说,其对于Binary的优点在于其在各大平台上的序列化和反序列化的标准都是一样的,不存在一个像Binary那样的大端或者小端问题。当然,我们的zookeeper采用的默认的序列化方式就是Binary二进制方式,这是因为二进制具有较好的性能,虽然大多数的平台对于二进制的实现方式都不尽相同。

zookeeper的网络协议

身为一个分布式的协调框架,zookeeper基于tcp|ip上实现了属于自己的网络通信协议

zookeeper的请求响应结构如下:
一个请求 = 请求头 + 请求体
一个响应 = 响应头 + 响应体

请求头和响应头的格式是固定的, 而请求体和响应体的格式会随着数据类型的变化而改变格式
请求和响应的结构都是通过java类来进行实现的,配合上我们的序列化传输方式,便可以愉快的进行传输

 class RequestHeader implements Record{
    
     // 实现record接口,以进行序列化
 	private int xid;  // 记录请求的发起顺序
 	private int type; // 记录请求的类型
	
}

请求体的话根据请求类型的不同,结构也分为很多种, 下面拿会话创建来举例子

	class ConnectRequest implements Record {
    
    
		private int protocolId; // 协议的版本的信息
		private int timout;   // 超时时间
		private long lastZxidSeen;  // 最后一个接收到服务器的zxid的序号
		private long sessionId;  // 会话的标示符 
		private byte[] passwd;// 会话的密码 

	}

响应头和请求头的结构类似,多了一个错误字段

public class replyHeader implements Recorder {
    
    
	private int xid;
	private int zxid;
	private int err;

}

响应体拿会话创建来举例子

public class ConnectResponse implements Recorder {
    
    
	private int protocolVersion;
	private int timeout;
	private long sessionId;
	private byte[] passwd;

}

以上就是zookeeper封装的的报文结构,是不是有了一个大致的概念了呢

猜你喜欢

转载自blog.csdn.net/weixin_44880685/article/details/113796818
今日推荐