使用protobuf与传统的stream作比较
1.协议模型:
package tutorial;
option java_package = "com.example.tutorial";
option java_outer_classname = "RoleTest";
message Role {
optional int64 id = 1;
optional string name = 2;
optional string email = 3;
}
即此协议包括id name email三个字段。
2.write写法比较
protobuf的写法如下
public static byte[] protobufEncode() throws Exception{
Builder builder = Role.newBuilder();
builder.setId(id);
builder.setName(name);
builder.setEmail(mail);
Role build = builder.build();
byte[] byteArray = build.toByteArray();
return byteArray;
}
stream的写法如下
public static byte[] streamEncode() throws Exception{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(bos);
output.writeLong(id);
output.writeUTF(name);
output.writeUTF(mail);
byte[] bytes = bos.toByteArray();
return bytes;
}
可以看出protobuf是不用在代码体现按照顺序write,而stream则需要。
3.read的写法比较
protobuf的写法如下
public static void protobufDecode(byte[] data) throws Exception{
Role role = Role.parseFrom(data);
System.err.println(role.getId()+",name:"+role.getName()+",email:"+role.getEmail());
}
stream的写法如下
public static void streamDecode(byte[] data) throws IOException{
ByteArrayInputStream bis = new ByteArrayInputStream(data);
DataInputStream input = new DataInputStream(bis);
long id = input.readLong();
String name = input.readUTF();
String email = input.readUTF();
System.err.println(id+",name:"+name+",email:"+email);
}
跟write一样,protobuf是不用按照顺序读,直接可以转换成一个对象。
4.write的特殊情况
有一些协议的第一个字段是表示结果(如回复添加好友)。在操作验证失败的情况下,一般客户端会
提示一些语句,如'此玩家已经是您的好友'。这种操作失败的协议通常除第一个字段以后的字段(好友id、名字、性别等)都是
可以忽略的,但是用普通的stream方式去read,一定要完整读完,否则会抛异常。
而protobuf则可以在定义协议的时候使用optional关键字表示此字段是可以不发送的,所以用protobuf,可以这样写
public static byte[] protobufEncode() throws Exception{
Builder builder = Role.newBuilder();
builder.setId(id);
//builder.setName(name);
//builder.setEmail(mail);
Role build = builder.build();
byte[] byteArray = build.toByteArray();
return byteArray;
}
读取的时候能够正常转换成对象,而没发送的字段按照字段类型的默认值来表示,如string默认是'',boolean是false,int32是0
5.流量对比
protobuf在压缩成byte[]的时候会动态计算字段的大小,但是会占用位,具体未看明白,看比较。
A.只发送整形的对比
用long型id,为以下情况
id:1
stream,len:8
[0, 0, 0, 0, 0, 0, 0, 1]
protobuf,len:2
[16, 1]
id:1000
stream,len:8
[0, 0, 0, 0, 0, 0, 3, -24]
protobuf,len:3
[16, -24, 7]
id:1000000
stream,len:8
[0, 0, 0, 0, 0, 15, 66, 64]
protobuf,len:4
[16, -64, -124, 61]
id:10000000000
stream,len:8
[0, 0, 0, 2, 84, 11, -28, 0]
protobuf,len:6
[16, -128, -56, -81, -96, 37]
id:10000000000000
stream,len:8
[0, 0, 9, 24, 78, 114, -96, 0]
protobuf,len:8
[16, -128, -64, -54, -13, -124, -93, 2]
id:1000000000000000
stream,len:8
[0, 3, -115, 126, -92, -58, -128, 0]
protobuf,len:9
[16, -128, -128, -102, -90, -22, -81, -29, 1]
id:100000000000000000
stream,len:8
[1, 99, 69, 120, 93, -118, 0, 0]
protobuf,len:10
[16, -128, -128, -88, -20, -123, -81, -47, -79, 1]
可以看出,当id数值小的时候,protobuf会比stream好,当数值到一定时,两者相同,当数值很大,protobuf会比stream差。
B.只发送字符串
name:k
stream,len:3
[0, 1, 107]
protobuf,len:3
[10, 1, 107]
name:kyle
stream,len:6
[0, 4, 107, 121, 108, 101]
protobuf,len:6
[10, 4, 107, 121, 108, 101]
name:kylekyle
stream,len:10
[0, 8, 107, 121, 108, 101, 107, 121, 108, 101]
protobuf,len:10
[10, 8, 107, 121, 108, 101, 107, 121, 108, 101]
name:kylekylekyle
stream,len:14
[0, 12, 107, 121, 108, 101, 107, 121, 108, 101, 107, 121, 108, 101]
protobuf,len:14
[10, 12, 107, 121, 108, 101, 107, 121, 108, 101, 107, 121, 108, 101]
可以看出,发送字符串是差不多的。
6.性能对比
发送id name email,作不同次数的读写循环对比,id选取两者流量相同10000000000000
id:10000000000000,name:kyle,email:
[email protected]
loop times:100
stream,spend:2 ms
protobuf,spend:30 ms
id:10000000000000,name:kyle,email:
[email protected]
loop times:10000
stream,spend:33 ms
protobuf,spend:81 ms
id:10000000000000,name:kyle,email:
[email protected]
loop times:100000
stream,spend:176 ms
protobuf,spend:178 ms
id:10000000000000,name:kyle,email:
[email protected]
loop times:1000000
stream,spend:1590 ms
protobuf,spend:1179 ms
比较奇怪,当循环数比较小时,stream领先,当循环数比较大时,protobuf领先