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

简介:

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

 

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

 

测试纬度

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

测试代码

准备protobuf文件

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.}  

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.}  

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

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代码

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[] { 1, 2, 3 }));  
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对象

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[] { 1, 2, 3 });  
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接口

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

统一的测试模板

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方法: 


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.    }  

最后相应的测试例子:

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版本基于二进制序列化的测试


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协议

 

 

测试代码:


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


相关文章
|
4月前
|
XML 存储 JSON
Twaver-HTML5基础学习(19)数据容器(2)_数据序列化_XML、Json
本文介绍了Twaver HTML5中的数据序列化,包括XML和JSON格式的序列化与反序列化方法。文章通过示例代码展示了如何将DataBox中的数据序列化为XML和JSON字符串,以及如何从这些字符串中反序列化数据,重建DataBox中的对象。此外,还提到了用户自定义属性的序列化注册方法。
58 1
|
2月前
|
JSON 数据格式 索引
Python中序列化/反序列化JSON格式的数据
【11月更文挑战第4天】本文介绍了 Python 中使用 `json` 模块进行序列化和反序列化的操作。序列化是指将 Python 对象(如字典、列表)转换为 JSON 字符串,主要使用 `json.dumps` 方法。示例包括基本的字典和列表序列化,以及自定义类的序列化。反序列化则是将 JSON 字符串转换回 Python 对象,使用 `json.loads` 方法。文中还提供了具体的代码示例,展示了如何处理不同类型的 Python 对象。
|
2月前
|
XML JSON Kubernetes
什么是 YAML?:一种简洁高效的数据序列化格式
什么是 YAML?:一种简洁高效的数据序列化格式
328 0
|
4月前
|
JSON 缓存 NoSQL
redis序列化数据时,如何包含clsss类型信息?
通过配置 `com.fasterxml.jackson.databind.ObjectMapper` 的 `enableDefaultTyping` 方法,可以使序列化后的 JSON 包含类信息。
66 2
|
5月前
|
存储 C# 数据库
解决C#对Firebase数据序列化失败的难题
在游戏开发中,Unity结合Firebase实时数据库为开发者提供强大支持,但在C#中进行数据序列化和反序列化时常遇难题。文章剖析了数据丢失或反序列化失败的原因,并给出解决方案,包括使用`JsonUtility`、确保字段标记为`[Serializable]`以及正确配置网络请求。示例代码演示了如何在Unity环境中实现Firebase数据的序列化和反序列化,并通过设置代理IP、Cookies和User-Agent来增强网络请求的安全性。这些技巧有助于确保数据完整传输,提升开发效率。
解决C#对Firebase数据序列化失败的难题
|
5月前
|
存储 分布式计算 Java
|
5月前
|
Java
JDK序列化原理问题之Hessian框架不支持writeObject/readObject方法如何解决
JDK序列化原理问题之Hessian框架不支持writeObject/readObject方法如何解决
|
5月前
|
存储 安全 Java
揭秘Java序列化神器Serializable:一键解锁对象穿越时空的超能力,你的数据旅行不再受限,震撼登场!
【8月更文挑战第4天】Serializable是Java中的魔术钥匙,开启对象穿越时空的能力。作为序列化的核心,它让复杂对象的复制与传输变得简单。通过实现此接口,对象能被序列化成字节流,实现本地存储或网络传输,再通过反序列化恢复原状。尽管使用方便,但序列化过程耗时且存在安全风险,需谨慎使用。
59 7
|
5月前
|
XML 存储 JSON
(十二)探索高性能通信与RPC框架基石:Json、ProtoBuf、Hessian序列化详解
如今这个分布式风靡的时代,网络通信技术,是每位技术人员必须掌握的技能,因为无论是哪种分布式技术,都离不开心跳、选举、节点感知、数据同步……等机制,而究其根本,这些技术的本质都是网络间的数据交互。正因如此,想要构建一个高性能的分布式组件/系统,不得不思考一个问题:怎么才能让数据传输的速度更快?
142 1
|
5月前
|
JSON 缓存 安全
Python pickle 二进制序列化和反序列化 - 数据持久化
Python pickle 二进制序列化和反序列化 - 数据持久化
78 0