proto3序列化很好用,在原来开发APP存数据到日志时,日志文档半小时可达300M,数据量大时对性能有很大影响,因此改用proto序列化存储数据,经测试性能有所提升,日志大小为原来三分之一,所以优势还是很明显的。
但proto3序列化多条消息到文件时,按官方文档介绍,反序列化时是没法区分一个完整对象序列化数据的界限的,也就是没有分隔符,因此需要自己设定分隔符,反序列化时按规则解即可,以下是基于上一篇文章中环境的案例
一、多消息持续序列化规则
每个对象序列化后字节数的长度作为序列化内容前置4个字节的数据,即若一条序列化数据长度为100个字节,那么前这个数据前加4个字节,这4个字节的内容是100,那么总长度就是104个字节,依此规则持续序列化到文件即可
二、定义proto数据结构
syntax = "proto3";
package tutorial;
message Person {
string name = 1;
int32 id = 2;
string email = 3;
string phone = 4;
}
三、序列化测试
/**
* 序列化
*
* @param path
*/
private void serializeProto(String path) {
System.out.println("serialize file " + path);
try {
OutputStream outputStream = new FileOutputStream(new File(path));
byte[] data;
for (int i = 0; i < 10; i++) {
Message.Person person = Message.Person.newBuilder().setId(i)
.setEmail(String.valueOf(i)).setName(String.valueOf(i)).setPhone(String.valueOf(i)).build();
byte[] dataByte = person.toByteArray();
byte[] lenByte = ProtoHelper.intToByteArray(dataByte.length);
data = new byte[dataByte.length + lenByte.length];
System.arraycopy(lenByte, 0, data, 0, lenByte.length);
System.arraycopy(dataByte, 0, data, lenByte.length, dataByte.length);
outputStream.write(data);
System.out.println("serialize len :" + dataByte.length + ",id:" + person.getId() + ",name:" + person.getName() + ",email:" + person.getEmail() + ",phone:" + person.getPhone());
}
outputStream.flush();
outputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
四、反序列化
因为上述数据量小,这里一次性从文件读出来,就没有设置缓冲区了
/**
* 反序列化
* @param path
*/
public void deSerializeProto(String path) {
System.out.println("deSerialize file " + path);
try {
InputStream inputStream = new FileInputStream(new File(path));
byte[] data = new byte[4096];
int len = 0;
while ((len = inputStream.read(data)) > 0) {
int index = 0;
while (index < len) {
byte[] lenByte = new byte[4];
System.arraycopy(data, index, lenByte, 0, 4);
int itemLen = ProtoHelper.byteArrayToInt(lenByte);
byte[] dataByte = new byte[itemLen];
System.arraycopy(data, index + lenByte.length, dataByte, 0, itemLen);
Message.Person person = Message.Person.parseFrom(dataByte);
System.out.println("deSerialize len :" + (dataByte.length + 4) + ",id:" + person.getId() + ",name:" + person.getName() + ",email:" + person.getEmail() + ",phone:" + person.getPhone());
index = index + lenByte.length + dataByte.length;
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
执行结果:
由于proto跨平台,所以java端序列化后,C端同事也能解码文件,有一定的日志安全性吧