几种序列化协议(protobuf,xstream,jackjson,jdk,hessian)相关数据对比

近研究了下google protobuf协议,顺便对比了一下json,xml,java序列化相关的数据对比,从几个纬度进行对比。

 

别人的相关测试数据: http://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking

 

测试纬度

  • 序列化时间
  • 反序列化时间
  • bytes大小

测试代码

准备protobuf文件

 

Message.proto文件代码   收藏代码
  1. import "InnerMessage.proto";  
  2. package demo;   
  3. option java_package = "com.agapple.protobuf.data";  
  4. option java_outer_classname = "MessageProtos";  
  5. option optimize_for = SPEED ;  //CODE_SIZE,LITE_RUNTIME  
  6. option java_generic_services = false;  
  7. message Message {  
  8.       
  9.     required string strObj = 1 [default="hello"];  
  10.     optional int32 int32Obj = 2;  
  11.     optional int64 int64Obj = 3;  
  12.     optional uint32 uint32Obj = 4;  
  13.     optional uint64 uint64Obj = 5;  
  14.     optional sint32 sint32Obj = 6;  
  15.     optional sint64 sint64Obj = 7;  
  16.     optional fixed32 fixed32Obj = 8;  
  17.     optional fixed64 fixed64Obj = 9;  
  18.     optional sfixed32 sfixed32Obj = 10;  
  19.     optional sfixed64 sfixed64Obj = 11;  
  20.     optional bool   boolObj = 12;  
  21.     optional bytes  bytesObj = 13;  
  22.     optional float folatObj = 14 [deprecated=true];  
  23.     repeated double doubleObj = 15 [packed=true]; //  
  24.     optional InnerMessage innerMessage = 16;  
  25. }  

 

 

Innermessage.proto代码   收藏代码
  1. import "EnumType.proto";  
  2.   
  3. package demo;   
  4. option java_package = "com.agapple.protobuf.data";  
  5. option java_outer_classname = "InnerMessageProtos";  
  6.   
  7. message InnerMessage {   
  8.     optional string name = 1 [default = "name"];  
  9.     optional int32 id = 2;  
  10.     optional EnumType type = 3 [default = UNIVERSAL];  
  11. }  

 

 

Enumtype.proto代码   收藏代码
  1. package demo;   
  2. option java_package = "com.agapple.protobuf.data";  
  3. option java_outer_classname = "EnumTypeProtos";  
  4.   
  5. enum EnumType {  
  6.     UNIVERSAL = 0;   
  7.     WEB = 1;   
  8.     IMAGES = 2;   
  9.     LOCAL = 3;   
  10.     NEWS = 4;   
  11.     PRODUCTS = 5;   
  12.     VIDEO = 6;   
  13. }  

 

 

基本上把protobuf支持的类型都囊括了,包括嵌套类型,枚举类型,以及各种int,uint,bool,bytes。  

 

依赖关系是Message.proto依赖了InnerMessage对象,而InnerMessage对象里包含了一个自定义枚举类型EnumType。

 

关于类型的使用可参见: 
      http://code.google.com/intl/zh/apis/protocolbuffers/docs/reference/java-generated.html
      http://code.google.com/intl/zh/apis/protocolbuffers/docs/proto.html

 

 

 

生成protobuf javabean

 

 

C代码   收藏代码
  1. cd /home/ljh/work/code/src/main/java  
  2.   
  3. /home/ljh/work/protobuf/bin/protoc --proto_path=com/agapple/protobuf/ --java_out=. com/agapple/protobuf/EnumType.proto  
  4. /home/ljh/work/protobuf/bin/protoc --proto_path=com/agapple/protobuf/ --java_out=. com/agapple/protobuf/InnerMessage.proto  
  5. /home/ljh/work/protobuf/bin/protoc --proto_path=com/agapple/protobuf/ --java_out=. com/agapple/protobuf/Message.proto  

 

 通过protobuf自带的protoc进行编译,指定了protobuf文件的路径, 具体的文档: http://code.google.com/intl/zh/apis/protocolbuffers/docs/proto.html#generating

 

 

运行脚本后就会生成对应的3个javabean文件: MessageProtos , InnerMessageProtos , EnumTypeProtos。

 

最后构造测试的protobuf bean代码

 

Java代码   收藏代码
  1. private static MessageProtos.Message getProtobufBean() {  
  2.         com.agapple.protobuf.data.MessageProtos.Message.Builder messageBuilder = MessageProtos.Message.newBuilder();  
  3.   
  4.         messageBuilder.setStrObj("message");  
  5.         messageBuilder.setFolatObj(1f);  
  6.         messageBuilder.addDoubleObj(1d);  
  7.         messageBuilder.addDoubleObj(2d);  
  8.         messageBuilder.setBoolObj(true);  
  9.   
  10.         messageBuilder.setBytesObj(ByteString.copyFrom(new byte[] { 123 }));  
  11.         messageBuilder.setInt32Obj(32);  
  12.         messageBuilder.setInt64Obj(64l);  
  13.         messageBuilder.setSint32Obj(232);  
  14.         messageBuilder.setSint64Obj(264);  
  15.         messageBuilder.setFixed32Obj(532);  
  16.         messageBuilder.setFixed64Obj(564);  
  17.         messageBuilder.setSfixed32Obj(2532);  
  18.         messageBuilder.setSfixed64Obj(2564);  
  19.         messageBuilder.setUint32Obj(632);  
  20.         messageBuilder.setUint64Obj(664);  
  21.   
  22.         com.agapple.protobuf.data.InnerMessageProtos.InnerMessage.Builder innerMessageBuilder = InnerMessageProtos.InnerMessage.newBuilder();  
  23.         innerMessageBuilder.setId(1);  
  24.         innerMessageBuilder.setName("inner");  
  25.         innerMessageBuilder.setType(EnumType.PRODUCTS);  
  26.   
  27.         messageBuilder.setInnerMessage(innerMessageBuilder);  
  28.   
  29.         return messageBuilder.build();  
  30.     }  

 

 

 

准备纯Pojo Bean 

同样的,为了和json , xml以及java序列化有个很好的对比,新建了3个纯的pojo bean:  MessagePojo , InnerMessagePojo , EnumTypePojo。

属性和proto的bean保持一致。

 

构建bean对象

 

Java代码   收藏代码
  1. private static MessagePojo getPojoBean() {  
  2.         MessagePojo bean = new MessagePojo();  
  3.   
  4.         bean.setStrObj("message");  
  5.         bean.setFolatObj(1f);  
  6.         List<Double> doubleObj = new ArrayList<Double>();  
  7.         doubleObj.add(1d);  
  8.         doubleObj.add(2d);  
  9.         bean.setDoubleObj(doubleObj);  
  10.         bean.setBoolObj(true);  
  11.   
  12.         bean.setBytesObj(new byte[] { 123 });  
  13.         bean.setInt32Obj(32);  
  14.         bean.setInt64Obj(64l);  
  15.         bean.setSint32Obj(232);  
  16.         bean.setSint64Obj(264);  
  17.         bean.setFixed32Obj(532);  
  18.         bean.setFixed64Obj(564);  
  19.         bean.setSfixed32Obj(2532);  
  20.         bean.setSfixed64Obj(2564);  
  21.         bean.setUint32Obj(632);  
  22.         bean.setUint64Obj(664);  
  23.   
  24.         InnerMessagePojo innerMessagePojo = new InnerMessagePojo();  
  25.         innerMessagePojo.setId(1);  
  26.         innerMessagePojo.setName("inner");  
  27.         innerMessagePojo.setType(EnumTypePojo.PRODUCTS);  
  28.   
  29.         bean.setInnerMessage(innerMessagePojo);  
  30.   
  31.         return bean;  
  32.     }  

 

 

 

具体的测试代码

定义测试Template接口

 

Java代码   收藏代码
  1. interface TestCallback {  
  2.   
  3.     String getName();  
  4.   
  5.     byte[] writeObject(Object source);  
  6.   
  7.     Object readObject(byte[] bytes);  
  8. }  

 

 

统一的测试模板

 

Java代码   收藏代码
  1. private static void testTemplate(TestCallback callback, Object source, int count) {  
  2.         int warmup = 10;  
  3.         // 先进行预热,加载一些类,避免影响测试  
  4.         for (int i = 0; i < warmup; i++) {  
  5.             byte[] bytes = callback.writeObject(source);  
  6.             callback.readObject(bytes);  
  7.         }  
  8.         restoreJvm(); // 进行GC回收  
  9.         // 进行测试  
  10.         long start = System.nanoTime();  
  11.         long size = 0l;  
  12.         for (int i = 0; i < count; i++) {  
  13.             byte[] bytes = callback.writeObject(source);  
  14.             size = size + bytes.length;  
  15.             callback.readObject(bytes);  
  16.             // System.out.println(callback.readObject(bytes));  
  17.             bytes = null;  
  18.         }  
  19.         long nscost = (System.nanoTime() - start);  
  20.         System.out.println(callback.getName() + " total cost=" + integerFormat.format(nscost) + "ns , each cost="  
  21.                            + integerFormat.format(nscost / count) + "ns , and byte sizes = " + size / count);  
  22.         restoreJvm();// 进行GC回收  
  23.   
  24.     }  

 

 

  在测试模板方法中,使用了warmup预热的概念,就是预先执行目标方法一定的次数,用于避免因为jit的优化影响系统测试。 同时包含了每次测试模板调用完成后system.gc保证下一轮的功能测试

 

  相应的restoreJvm方法: 

Java代码   收藏代码
  1. private static void restoreJvm() {  
  2.         int maxRestoreJvmLoops = 10;  
  3.         long memUsedPrev = memoryUsed();  
  4.         for (int i = 0; i < maxRestoreJvmLoops; i++) {  
  5.             System.runFinalization();  
  6.             System.gc();  
  7.   
  8.             long memUsedNow = memoryUsed();  
  9.             // break early if have no more finalization and get constant mem used  
  10.             if ((ManagementFactory.getMemoryMXBean().getObjectPendingFinalizationCount() == 0)  
  11.                 && (memUsedNow >= memUsedPrev)) {  
  12.                 break;  
  13.             } else {  
  14.                 memUsedPrev = memUsedNow;  
  15.             }  
  16.         }  
  17.     }  
  18.   
  19.     private static long memoryUsed() {  
  20.         Runtime rt = Runtime.getRuntime();  
  21.         return rt.totalMemory() - rt.freeMemory();  
  22.     }  

 

最后相应的测试例子:

 

Java代码   收藏代码
  1. final int testCount = 1000 * 500;          
  2. final MessageProtos.Message protoObj = getProtobufBean();  
  3. final MessagePojo pojoOBj = getPojoBean();  
  4.   
  5. // Serializable测试  
  6. testTemplate(new TestCallback() {  
  7.   
  8.     public String getName() {  
  9.         return "Serializable Test";  
  10.     }  
  11.   
  12.     @Override  
  13.     public byte[] writeObject(Object source) {  
  14.         try {  
  15.             ByteArrayOutputStream bout = new ByteArrayOutputStream();  
  16.             ObjectOutputStream output = new ObjectOutputStream(bout);  
  17.             output.writeObject(source);  
  18.             return bout.toByteArray();  
  19.         } catch (IOException e) {  
  20.             e.printStackTrace();  
  21.         }  
  22.         return null;  
  23.     }  
  24.   
  25.     @Override  
  26.     public Object readObject(byte[] bytes) {  
  27.         try {  
  28.             ByteArrayInputStream bin = new ByteArrayInputStream(bytes);  
  29.             ObjectInputStream input = new ObjectInputStream(bin);  
  30.             return input.readObject();  
  31.         } catch (Exception e) {  
  32.             e.printStackTrace();  
  33.         }  
  34.         return null;  
  35.     }  
  36. }, pojoOBj, testCount);  
  37.   
  38. // protobuf测试  
  39. testTemplate(new TestCallback() {  
  40.   
  41.     public String getName() {  
  42.         return "protobuf test";  
  43.     }  
  44.   
  45.     @Override  
  46.     public byte[] writeObject(Object source) {  
  47.         if (source instanceof MessageProtos.Message) {  
  48.             MessageProtos.Message message = (MessageProtos.Message) source;  
  49.             return message.toByteArray();  
  50.         }  
  51.   
  52.         return null;  
  53.     }  
  54.   
  55.     @Override  
  56.     public Object readObject(byte[] bytes) {  
  57.         try {  
  58.             return MessageProtos.Message.parseFrom(bytes);  
  59.         } catch (InvalidProtocolBufferException e) {  
  60.             e.printStackTrace();  
  61.         }  
  62.         return null;  
  63.     }  
  64. }, protoObj, testCount);  
  65.   
  66. // json测试  
  67. final ObjectMapper objectMapper = new ObjectMapper();  
  68. final JavaType javaType = TypeFactory.type(pojoOBj.getClass());  
  69.   
  70. // JSON configuration not to serialize null field  
  71. objectMapper.getSerializationConfig().setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);  
  72.   
  73. // JSON configuration not to throw exception on empty bean class  
  74. objectMapper.getSerializationConfig().disable(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS);  
  75.   
  76. // JSON configuration for compatibility  
  77. objectMapper.configure(Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);  
  78. objectMapper.configure(Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);  
  79.   
  80. testTemplate(new TestCallback() {  
  81.   
  82.     public String getName() {  
  83.         return "Jackson Test";  
  84.     }  
  85.   
  86.     @Override  
  87.     public byte[] writeObject(Object source) {  
  88.         try {  
  89.             return objectMapper.writeValueAsBytes(source);  
  90.         } catch (JsonGenerationException e) {  
  91.             e.printStackTrace();  
  92.         } catch (JsonMappingException e) {  
  93.             e.printStackTrace();  
  94.         } catch (IOException e) {  
  95.             e.printStackTrace();  
  96.         }  
  97.   
  98.         return null;  
  99.     }  
  100.   
  101.     @Override  
  102.     public Object readObject(byte[] bytes) {  
  103.         try {  
  104.             return objectMapper.readValue(bytes, 0, bytes.length, javaType);  
  105.         } catch (JsonParseException e) {  
  106.             e.printStackTrace();  
  107.         } catch (JsonMappingException e) {  
  108.             e.printStackTrace();  
  109.         } catch (IOException e) {  
  110.             e.printStackTrace();  
  111.         }  
  112.         return null;  
  113.     }  
  114. }, pojoOBj, testCount);  
  115.   
  116. // Xstream测试  
  117. final XStream xstream = new XStream();  
  118. testTemplate(new TestCallback() {  
  119.   
  120.     public String getName() {  
  121.         return "Xstream test";  
  122.     }  
  123.   
  124.     @Override  
  125.     public byte[] writeObject(Object source) {  
  126.         return xstream.toXML(source).getBytes();  
  127.     }  
  128.   
  129.     @Override  
  130.     public Object readObject(byte[] bytes) {  
  131.         return xstream.fromXML(new ByteArrayInputStream(bytes));  
  132.     }  
  133. }, pojoOBj, testCount);  

 

 

2011年3月10号补充 =========================================================

增加了hessian 3.1.5版本基于二进制序列化的测试

 

Xml代码   收藏代码
  1. <dependency>  
  2.     <groupId>com.caucho</groupId>  
  3.     <artifactId>hessian</artifactId>  
  4.     <version>3.1.5</version>  
  5. </dependency>  

 

 

测试了3种情况:

 

  1. hessian 2协议
  2. hessian 2协议 + deflat压缩
  3. hessian 1协议

 

 

测试代码:

 

Java代码   收藏代码
  1. // hessian 2 with no deflat  
  2.         testTemplate(new TestCallback() {  
  3.   
  4.             public String getName() {  
  5.                 return "hessian 2 with no deflat";  
  6.             }  
  7.   
  8.             @Override  
  9.             public byte[] writeObject(Object source) {  
  10.                 try {  
  11.                     ByteArrayOutputStream bos = new ByteArrayOutputStream();  
  12.                     Hessian2Output out = new Hessian2Output(bos);  
  13.                     // out.startMessage();  
  14.                     out.writeObject(source);  
  15.                     // out.completeMessage();  
  16.                     out.flush();  
  17.                     return bos.toByteArray();  
  18.                 } catch (IOException e) {  
  19.                     e.printStackTrace();  
  20.                 }  
  21.                 return null;  
  22.             }  
  23.   
  24.             @Override  
  25.             public Object readObject(byte[] bytes) {  
  26.                 try {  
  27.                     ByteArrayInputStream bin = new ByteArrayInputStream(bytes);  
  28.                     Hessian2Input in = new Hessian2Input(bin);  
  29.                     // in.startMessage();  
  30.                     Object obj = in.readObject();  
  31.                     // in.completeMessage();  
  32.                     return obj;  
  33.                 } catch (IOException e) {  
  34.                     e.printStackTrace();  
  35.                 }  
  36.                 return null;  
  37.             }  
  38.         }, pojoOBj, testCount);  
  39.   
  40.         // hessian 2 with deflat  
  41.         final Deflation envelope = new Deflation();  
  42.         testTemplate(new TestCallback() {  
  43.   
  44.             public String getName() {  
  45.                 return "hessian 2 with deflat";  
  46.             }  
  47.   
  48.             @Override  
  49.             public byte[] writeObject(Object source) {  
  50.                 try {  
  51.                     ByteArrayOutputStream bos = new ByteArrayOutputStream();  
  52.                     Hessian2Output out = new Hessian2Output(bos);  
  53.                     out = envelope.wrap(out);  
  54.                     out.writeObject(source);  
  55.                     out.flush();  
  56.                     out.close(); // 记得关闭  
  57.                     return bos.toByteArray();  
  58.                 } catch (Exception e) {  
  59.                     e.printStackTrace();  
  60.                 }  
  61.                 return null;  
  62.             }  
  63.   
  64.             @Override  
  65.             public Object readObject(byte[] bytes) {  
  66.                 try {  
  67.                     ByteArrayInputStream bin = new ByteArrayInputStream(bytes);  
  68.                     Hessian2Input in = new Hessian2Input(bin);  
  69.                     in = envelope.unwrap(in);  
  70.                     Object obj = in.readObject();  
  71.                     in.close();  
  72.                     return obj;  
  73.                 } catch (IOException e) {  
  74.                     e.printStackTrace();  
  75.                 }  
  76.                 return null;  
  77.             }  
  78.         }, pojoOBj, testCount);  
  79.   
  80.         // hessian 1 with no deflat  
  81.         testTemplate(new TestCallback() {  
  82.   
  83.             public String getName() {  
  84.                 return "hessian 1 with no deflat";  
  85.             }  
  86.   
  87.             @Override  
  88.             public byte[] writeObject(Object source) {  
  89.                 try {  
  90.                     ByteArrayOutputStream bos = new ByteArrayOutputStream();  
  91.                     HessianOutput out = new HessianOutput(bos);  
  92.                     out.writeObject(source);  
  93.                     out.flush();  
  94.                     return bos.toByteArray();  
  95.                 } catch (Exception e) {  
  96.                     e.printStackTrace();  
  97.                 }  
  98.                 return null;  
  99.             }  
  100.   
  101.             @Override  
  102.             public Object readObject(byte[] bytes) {  
  103.                 try {  
  104.                     ByteArrayInputStream bin = new ByteArrayInputStream(bytes);  
  105.                     HessianInput in = new HessianInput(bin);  
  106.                     Object obj = in.readObject();  
  107.                     in.close();  
  108.                     return obj;  
  109.                 } catch (IOException e) {  
  110.                     e.printStackTrace();  
  111.                 }  
  112.                 return null;  
  113.             }  
  114.         }, pojoOBj, testCount);  
 

 


测试结果

序列化数据对比



 

bytes字节数对比

具体的数字: 

 protobufjacksonxstreamSerializablehessian2hessian2压缩hessian1
序列化(单位ns)11545421 92406 101892679410076629027
反序列化(单位ns)13348743 117329 640273787118843237596
bytes97311 664 824374283495

 

  1. protobuf 不管是处理时间上,还是空间占用上都优于现有的其他序列化方式。内存暂用是java 序列化的1/9,时间也是差了一个数量级,一次操作在1us左右。缺点:就是对象结构体有限制,只适合于内部系统使用。
  2. json格式在空间占用还是有一些优势,是java序列化的1/2.6。序列化和反序列化处理时间上差不多,也就在5us。当然这次使用的jackson,如果使用普通的jsonlib可能没有这样好的性能,jsonlib估计跟java序列化差不多。
  3. xml相比于java序列化来说,空间占用上有点优势,但不明显。处理时间上比java序列化多了一个数量级,在100us左右。
  4. 以前一种的java序列化,表现得有些失望
  5. hessian测试有点意外,具体序列化数据上还步入json。性能上也不如jackjson,输得比较彻底。
  6. hessian使用压缩,虽然在字节上有20%以上的空间提升,但性能上差了4,5倍,典型的以时间换空间。总的来说还是google protobuf比较给力

 

总结 

以后在内部系统,数据cache存储上可以考虑使用protobuf。跟外部系统交互上可以考虑使用json。

 

有兴趣的同学,可以研究一下google protobuf的marshall的方式: http://code.google.com/intl/zh/apis/protocolbuffers/docs/encoding.html


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值