幾種序列化協議(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字節數對比

具體的數字: 

  protobuf jackson xstream Serializable hessian2 hessian2壓縮 hessian1
序列化(單位ns) 1154 5421  92406  10189 26794 100766 29027
反序列化(單位ns) 1334 8743  117329  64027 37871 188432 37596
bytes 97 311  664  824 374 283 495

 

  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

發佈了474 篇原創文章 · 獲贊 208 · 訪問量 300萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章