上篇文章分析到了zookeeper集群之间通信时用到了jute序列化协议,本篇文章来简单了解jute如何序列化和反序列化以及如何解决粘包拆包的问题的
今天上网看了一下查了一下zookeeper jute,其实网上也有很多这方面的资料,我就简单的写了一个demo先来展示一下,最后给大家一点自己如何自定义传输协议,如何解决粘包拆包的问题
public class City implements Record{
private String cityNo;//城市编号
private String cityName;//城市名称
public City() {
}
public City(String cityNo, String cityName) {
this.cityNo = cityNo;
this.cityName = cityName;
}
@Override
public void serialize(OutputArchive archive, String tag) throws IOException {
archive.startRecord(this, tag);
archive.writeString(cityNo, "cityNo");
archive.writeString(cityName, "cityName");
archive.endRecord(this, tag);
}
@Override
public void deserialize(InputArchive archive, String tag) throws IOException {
archive.startRecord(tag);
this.cityNo = archive.readString("cityNo");
this.cityName = archive.readString("cityName");
archive.endRecord(tag);
}
public String getCityNo() {
return cityNo;
}
public void setCityNo(String cityNo) {
this.cityNo = cityNo;
}
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
@Override
public String toString() {
return "City{" +
"cityNo='" + cityNo + '\'' +
", cityName='" + cityName + '\'' +
'}';
}
}
自定义一个对象,必须实现Record接口,我们看一下Record接口的代码
public interface Record {
public void serialize(OutputArchive archive, String tag)
throws IOException;
public void deserialize(InputArchive archive, String tag)
throws IOException;
}
它只有两个方法,serialize方法是对数据进行序列化, deserialize对数据进行反序列化
然后OutputArchive就是对各种类型的数据进行write,相反InputArchive对各种数据进行读取,tag就是可以指定一下读取到哪个标记算是一个整体,然后对数据进行反序列化,底层主要就是根据tag来解决粘包问题的
看一下测试类
public class MainTest {
public static void main(String[] args) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos);
City city = new City("bj", "北京");
city.serialize(boa, "city");
ByteBuffer array = ByteBuffer.wrap(baos.toByteArray());
ByteArrayInputStream bais = new ByteArrayInputStream(array.array());
BinaryInputArchive bia = BinaryInputArchive.getArchive(bais);
City newCity = new City();
newCity.deserialize(bia, "city");
System.out.println(newCity);
bais.close();
baos.close();
}
}
上面进行输出内容
City{cityNo='bj', cityName='北京'}
其实对对象进行序列化的协议有很多,比如protobuf、thrift、dubbo自定义的协议、hessian协议等等
看过dubbo源码的小伙伴应该对dubbo协议知道,它是如何自定义协议的?
粘贴一张官网的图
也粘贴个官网链接吧 http://dubbo.apache.org/zh-cn/docs/dev/implementation.html,大家感兴趣的可以深入去看看
说一下我的简单理解,其实就是写一堆的数据,一个int有4个字节,long有8个字节,然后每个字节中的每一位代表什么意思,比如上图中第16位的地方,然后用0代表请求,用1代表响应,然后去解析这个地方的值,你就知道当前是请求还是响应了,自定义序列化协议需要客户端和服务端上方协商好一致性,该如何解析等等
那我们该如何自定义序列化协议呢,我这里就简单的抛砖引玉一下,有兴趣的可以深入思考一下
比如,客户端发送 “张三” 这个字符串给服务端,该怎么发送呢?
先把“张三”这个字符串转成字节数组 byte array
可以这样,比如我先写一个int 4个字节长度的 请求类型writeInt(0) 0表示请求, 1表示响应 然后我再写一个int 4个字节的要发送数据的长度(字节数组长度), 比如writeInt(100), 接着写要发送的数据 writeBytes(“张三”.toByteArray()) 这是模拟的。然后发送个服务端
那服务端如何解析呢?它首先读取int,比如readInt(),把0 读取出来,知道了当前是一个请求, 然后在读取一个int,readInt()把数据的长度读取出来,此时是100,然后接着读取,比如readBytes,此时要指定读取的长度为100,如果超过100,只能读取100长度,如果不够100,则等待下次请求继续读取,直到两次加起来达到100之后,再去反序列化,此时就能得到"张三"了
大家有没有明白?仔细细品一下
通过上面的方式就可以解决我们在传输过程当中的粘包拆包问题。只要涉及到网络传输,必然涉及到粘包拆包的问题,比如netty有几种解决粘包拆包的问题,我们可以指定长度、或者指定结尾符号来等等,其实底层也是上面的实现原理,只是深度包装了一下
好了,本篇文章就说这么多吧,上篇文章分析到了leader启动之后做了什么事,到监听其他follower来连接发送数据。下一篇我们分析一下follower启动的时候都做了什么,如何和leader建立连接的