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

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

 

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

 

测试纬度

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

测试代码

准备protobuf文件

 

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

 

 

import "EnumType.proto";

package demo; 
option java_package = "com.agapple.protobuf.data";
option java_outer_classname = "InnerMessageProtos";

message InnerMessage { 
	optional string name = 1 [default = "name"];
	optional int32 id = 2;
	optional EnumType type = 3 [default = UNIVERSAL];
}

 

 

package demo; 
option java_package = "com.agapple.protobuf.data";
option java_outer_classname = "EnumTypeProtos";

enum EnumType {
	UNIVERSAL = 0; 
	WEB = 1; 
	IMAGES = 2; 
	LOCAL = 3; 
	NEWS = 4; 
	PRODUCTS = 5; 
	VIDEO = 6; 
}

 

 

基本上把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

 

 

cd /home/ljh/work/code/src/main/java

/home/ljh/work/protobuf/bin/protoc --proto_path=com/agapple/protobuf/ --java_out=. com/agapple/protobuf/EnumType.proto
/home/ljh/work/protobuf/bin/protoc --proto_path=com/agapple/protobuf/ --java_out=. com/agapple/protobuf/InnerMessage.proto
/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代码

 

private static MessageProtos.Message getProtobufBean() {
        com.agapple.protobuf.data.MessageProtos.Message.Builder messageBuilder = MessageProtos.Message.newBuilder();

        messageBuilder.setStrObj("message");
        messageBuilder.setFolatObj(1f);
        messageBuilder.addDoubleObj(1d);
        messageBuilder.addDoubleObj(2d);
        messageBuilder.setBoolObj(true);

        messageBuilder.setBytesObj(ByteString.copyFrom(new byte[] { 1, 2, 3 }));
        messageBuilder.setInt32Obj(32);
        messageBuilder.setInt64Obj(64l);
        messageBuilder.setSint32Obj(232);
        messageBuilder.setSint64Obj(264);
        messageBuilder.setFixed32Obj(532);
        messageBuilder.setFixed64Obj(564);
        messageBuilder.setSfixed32Obj(2532);
        messageBuilder.setSfixed64Obj(2564);
        messageBuilder.setUint32Obj(632);
        messageBuilder.setUint64Obj(664);

        com.agapple.protobuf.data.InnerMessageProtos.InnerMessage.Builder innerMessageBuilder = InnerMessageProtos.InnerMessage.newBuilder();
        innerMessageBuilder.setId(1);
        innerMessageBuilder.setName("inner");
        innerMessageBuilder.setType(EnumType.PRODUCTS);

        messageBuilder.setInnerMessage(innerMessageBuilder);

        return messageBuilder.build();
    }

 

 

 

准备纯Pojo Bean 

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

属性和proto的bean保持一致。

 

构建bean对象

 

private static MessagePojo getPojoBean() {
        MessagePojo bean = new MessagePojo();

        bean.setStrObj("message");
        bean.setFolatObj(1f);
        List<Double> doubleObj = new ArrayList<Double>();
        doubleObj.add(1d);
        doubleObj.add(2d);
        bean.setDoubleObj(doubleObj);
        bean.setBoolObj(true);

        bean.setBytesObj(new byte[] { 1, 2, 3 });
        bean.setInt32Obj(32);
        bean.setInt64Obj(64l);
        bean.setSint32Obj(232);
        bean.setSint64Obj(264);
        bean.setFixed32Obj(532);
        bean.setFixed64Obj(564);
        bean.setSfixed32Obj(2532);
        bean.setSfixed64Obj(2564);
        bean.setUint32Obj(632);
        bean.setUint64Obj(664);

        InnerMessagePojo innerMessagePojo = new InnerMessagePojo();
        innerMessagePojo.setId(1);
        innerMessagePojo.setName("inner");
        innerMessagePojo.setType(EnumTypePojo.PRODUCTS);

        bean.setInnerMessage(innerMessagePojo);

        return bean;
    }

 

 

 

具体的测试代码

定义测试Template接口

 

interface TestCallback {

    String getName();

    byte[] writeObject(Object source);

    Object readObject(byte[] bytes);
}

 

 

统一的测试模板

 

private static void testTemplate(TestCallback callback, Object source, int count) {
        int warmup = 10;
        // 先进行预热,加载一些类,避免影响测试
        for (int i = 0; i < warmup; i++) {
            byte[] bytes = callback.writeObject(source);
            callback.readObject(bytes);
        }
        restoreJvm(); // 进行GC回收
        // 进行测试
        long start = System.nanoTime();
        long size = 0l;
        for (int i = 0; i < count; i++) {
            byte[] bytes = callback.writeObject(source);
            size = size + bytes.length;
            callback.readObject(bytes);
            // System.out.println(callback.readObject(bytes));
            bytes = null;
        }
        long nscost = (System.nanoTime() - start);
        System.out.println(callback.getName() + " total cost=" + integerFormat.format(nscost) + "ns , each cost="
                           + integerFormat.format(nscost / count) + "ns , and byte sizes = " + size / count);
        restoreJvm();// 进行GC回收

    }

 

 

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

 

  相应的restoreJvm方法: 

private static void restoreJvm() {
        int maxRestoreJvmLoops = 10;
        long memUsedPrev = memoryUsed();
        for (int i = 0; i < maxRestoreJvmLoops; i++) {
            System.runFinalization();
            System.gc();

            long memUsedNow = memoryUsed();
            // break early if have no more finalization and get constant mem used
            if ((ManagementFactory.getMemoryMXBean().getObjectPendingFinalizationCount() == 0)
                && (memUsedNow >= memUsedPrev)) {
                break;
            } else {
                memUsedPrev = memUsedNow;
            }
        }
    }

    private static long memoryUsed() {
        Runtime rt = Runtime.getRuntime();
        return rt.totalMemory() - rt.freeMemory();
    }

 

最后相应的测试例子:

 

        final int testCount = 1000 * 500;        
        final MessageProtos.Message protoObj = getProtobufBean();
        final MessagePojo pojoOBj = getPojoBean();

        // Serializable测试
        testTemplate(new TestCallback() {

            public String getName() {
                return "Serializable Test";
            }

            @Override
            public byte[] writeObject(Object source) {
                try {
                    ByteArrayOutputStream bout = new ByteArrayOutputStream();
                    ObjectOutputStream output = new ObjectOutputStream(bout);
                    output.writeObject(source);
                    return bout.toByteArray();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }

            @Override
            public Object readObject(byte[] bytes) {
                try {
                    ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
                    ObjectInputStream input = new ObjectInputStream(bin);
                    return input.readObject();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }
        }, pojoOBj, testCount);

        // protobuf测试
        testTemplate(new TestCallback() {

            public String getName() {
                return "protobuf test";
            }

            @Override
            public byte[] writeObject(Object source) {
                if (source instanceof MessageProtos.Message) {
                    MessageProtos.Message message = (MessageProtos.Message) source;
                    return message.toByteArray();
                }

                return null;
            }

            @Override
            public Object readObject(byte[] bytes) {
                try {
                    return MessageProtos.Message.parseFrom(bytes);
                } catch (InvalidProtocolBufferException e) {
                    e.printStackTrace();
                }
                return null;
            }
        }, protoObj, testCount);

        // json测试
        final ObjectMapper objectMapper = new ObjectMapper();
        final JavaType javaType = TypeFactory.type(pojoOBj.getClass());

        // JSON configuration not to serialize null field
        objectMapper.getSerializationConfig().setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);

        // JSON configuration not to throw exception on empty bean class
        objectMapper.getSerializationConfig().disable(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS);

        // JSON configuration for compatibility
        objectMapper.configure(Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
        objectMapper.configure(Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);

        testTemplate(new TestCallback() {

            public String getName() {
                return "Jackson Test";
            }

            @Override
            public byte[] writeObject(Object source) {
                try {
                    return objectMapper.writeValueAsBytes(source);
                } catch (JsonGenerationException e) {
                    e.printStackTrace();
                } catch (JsonMappingException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }

                return null;
            }

            @Override
            public Object readObject(byte[] bytes) {
                try {
                    return objectMapper.readValue(bytes, 0, bytes.length, javaType);
                } catch (JsonParseException e) {
                    e.printStackTrace();
                } catch (JsonMappingException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }
        }, pojoOBj, testCount);

        // Xstream测试
        final XStream xstream = new XStream();
        testTemplate(new TestCallback() {

            public String getName() {
                return "Xstream test";
            }

            @Override
            public byte[] writeObject(Object source) {
                return xstream.toXML(source).getBytes();
            }

            @Override
            public Object readObject(byte[] bytes) {
                return xstream.fromXML(new ByteArrayInputStream(bytes));
            }
        }, pojoOBj, testCount);

 

 

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

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

 

<dependency>
    <groupId>com.caucho</groupId>
    <artifactId>hessian</artifactId>
    <version>3.1.5</version>
</dependency>

 

 

测试了3种情况:

 

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

 

 

测试代码:

 

// hessian 2 with no deflat
        testTemplate(new TestCallback() {

            public String getName() {
                return "hessian 2 with no deflat";
            }

            @Override
            public byte[] writeObject(Object source) {
                try {
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    Hessian2Output out = new Hessian2Output(bos);
                    // out.startMessage();
                    out.writeObject(source);
                    // out.completeMessage();
                    out.flush();
                    return bos.toByteArray();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }

            @Override
            public Object readObject(byte[] bytes) {
                try {
                    ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
                    Hessian2Input in = new Hessian2Input(bin);
                    // in.startMessage();
                    Object obj = in.readObject();
                    // in.completeMessage();
                    return obj;
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }
        }, pojoOBj, testCount);

        // hessian 2 with deflat
        final Deflation envelope = new Deflation();
        testTemplate(new TestCallback() {

            public String getName() {
                return "hessian 2 with deflat";
            }

            @Override
            public byte[] writeObject(Object source) {
                try {
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    Hessian2Output out = new Hessian2Output(bos);
                    out = envelope.wrap(out);
                    out.writeObject(source);
                    out.flush();
                    out.close(); // 记得关闭
                    return bos.toByteArray();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }

            @Override
            public Object readObject(byte[] bytes) {
                try {
                    ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
                    Hessian2Input in = new Hessian2Input(bin);
                    in = envelope.unwrap(in);
                    Object obj = in.readObject();
                    in.close();
                    return obj;
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }
        }, pojoOBj, testCount);

        // hessian 1 with no deflat
        testTemplate(new TestCallback() {

            public String getName() {
                return "hessian 1 with no deflat";
            }

            @Override
            public byte[] writeObject(Object source) {
                try {
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    HessianOutput out = new HessianOutput(bos);
                    out.writeObject(source);
                    out.flush();
                    return bos.toByteArray();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }

            @Override
            public Object readObject(byte[] bytes) {
                try {
                    ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
                    HessianInput in = new HessianInput(bin);
                    Object obj = in.readObject();
                    in.close();
                    return obj;
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }
        }, 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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值