通讯协议序列化解读(二) protostuff详解教程

上一篇文章 通讯协议序列化解读(一):http://www.cnblogs.com/tohxyblog/p/8974641.html 

前言:上一面文章我们介绍了java序列化,以及谷歌protobuf,但是由于protobuf的使用起来并不像其他序列化那么简单(首先要写.proto文件,然后编译.proto文件,生成对应的.java文件),所以即使他是如何的优秀,也还是没能抢占json的份额。 这篇文章我们要介绍的是一款基于protobuf的java序列化协议——prorostuff,在java端能极大的简便使用,而且反序列化可由protobuf完成(那么前端就可以用其他语言的protobuf解码)。

一、protostuff介绍

protostuff 基于Google protobuf,但是提供了更多的功能和更简易的用法。其中,protostuff-runtime 实现了无需预编译对java bean进行protobuf序列化/反序列化的能力。protostuff-runtime的局限是序列化前需预先传入schema,反序列化不负责对象的创建只负责复制,因而必须提供默认构造函数。此外,protostuff 还可以按照protobuf的配置序列化成json/yaml/xml等格式。

在性能上,protostuff不输原生的protobuf,甚至有反超之势。

二、Protostuff特征

  1. 支持protostuff-compiler产生的消息
  2. 支持现有的POJO
  3. 支持现有的protoc产生的Java消息
  4. 与各种移动平台的互操作能力(Android、Kindle、j2me)
  5. 支持转码

三、工具类实现

导包:

<!-- //protostuff序列化 -->
	<dependency>  
     <groupId>com.dyuproject.protostuff</groupId>  
     <artifactId>protostuff-core</artifactId>  
     <version>1.0.8</version>  
 </dependency>  
 <dependency>  
     <groupId>com.dyuproject.protostuff</groupId>  
     <artifactId>protostuff-runtime</artifactId>  
     <version>1.0.8</version>  
 </dependency>  
 <!-- Objenesis -->
 <dependency>
     <groupId>org.objenesis</groupId>
     <artifactId>objenesis</artifactId>
     <version>2.1</version>
 </dependency>

工具类:

package com.result.base.tools;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.objenesis.Objenesis;
import org.objenesis.ObjenesisStd;

import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtobufIOUtil;
import com.dyuproject.protostuff.ProtostuffIOUtil;
import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.runtime.RuntimeSchema;


/** 
* @author 作者 huangxinyu 
* @version 创建时间:2018年1月9日 下午7:41:24 
* Protostuff序列化工具
*/
public class SerializationUtil {
	private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<>();

    private static Objenesis objenesis = new ObjenesisStd(true);

    private SerializationUtil() {
    }

    @SuppressWarnings("unchecked")
    private static <T> Schema<T> getSchema(Class<T> cls) {
        Schema<T> schema = (Schema<T>) cachedSchema.get(cls);
        if (schema == null) {
            schema = RuntimeSchema.createFrom(cls);
            if (schema != null) {
                cachedSchema.put(cls, schema);
            }
        }
        return schema;
    }

    @SuppressWarnings("unchecked")
    public static <T> String serializeToString(T obj) {
        Class<T> cls = (Class<T>) obj.getClass();
        LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        try {
            Schema<T> schema = getSchema(cls);
            return new String(ProtobufIOUtil.toByteArray(obj, schema, buffer), "ISO8859-1");
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        } finally {
            buffer.clear();
        }
    }

    public static <T> T deserializeFromString(String data, Class<T> cls) {
        try {
            T message = (T) objenesis.newInstance(cls);
            Schema<T> schema = getSchema(cls);
            ProtobufIOUtil.mergeFrom(data.getBytes("ISO8859-1"), message, schema);
            return message;
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }
    

    @SuppressWarnings("unchecked")
    public static <T> byte[] serializeToByte(T obj) {
        Class<T> cls = (Class<T>) obj.getClass();
        LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        try {
            Schema<T> schema = getSchema(cls);
            return ProtobufIOUtil.toByteArray(obj, schema, buffer);
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        } finally {
            buffer.clear();
        }
    }

    public static <T> T deserializeFromByte(byte[] data, Class<T> cls) {
        try {
            T message = (T) objenesis.newInstance(cls);
            Schema<T> schema = getSchema(cls);
            ProtobufIOUtil.mergeFrom(data, message, schema);
            return message;
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }
}

四、性能测试

4.1 测试环境

xstraem版本:1.3.1

protobuf-java版本:3.0.0-alpha-2

java版本:1.7

-Xms2048m

-Xmx2048m

4.2 测试工具

用时: 控制台输出时间

CPU&内存: jconsole

文件大小: 文件属性

4.3 说明

测试中,xml和protoBuf和protostuff三种测试所使用的JavaBean所拥有的字段类型相同、字段数量相同(约28个)、字段所附的值相同、都包含有一个List<String>字段,用List字段的size来控制JavaBean对象的大小。本次测试中size=100

4.4  结果

测试A:10000个对象

xstream

protobuf

protostuff

序列化

用时(ms)

2399

648

261

占用的CPU(%)

24.2

12.3

3.4

占用的内存(M)

154

235

92

每个文件大小(byte)

2822

574

574

反序列化

用时(ms)

3378

167

224

占用CPU(%)

15.9

14.2

6.1

占用内存(M)

248

307

164

备注:10000个对象

测试B:25000个对象

xstream

protobuf

protostuff

序列化

用时(ms)

4161

767

293

占用的CPU(%)

31.2

14.6

4.7

占用的内存(M)

495

228

194

每个文件大小(byte)

2822

574

574

反序列化

用时(ms)

6941

252

393

占用CPU(%)

31.9

21.9

8.1

占用内存(M)

411

382

348

备注:25000个对象

测试C:100000个对象

xstream

protobuf

protostuff

序列化

用时(ms)

12867

3070

704

占用的CPU(%)

42.5

44.9

22.3

占用的内存(M)

1098

1058

572

每个文件大小(byte)

2822

574

574

反序列化

用时(ms)

24442

4540

1522

占用CPU(%)

38.8

68.2

24.1

占用内存(M)

2215

597

870

备注:50000个对象

引用最后一组数据的直方图:

4.5 结论

1、序列化:

  1.1、速度上:protostuff比protobuf快3倍左右,protobuf比xml快4-5倍,该倍数随着序列化对象的增加,基本保持不变。

  1.2、CPU上:protostuff占用最少,protobuf其次,xml最后。

  1.3、内存上:protostuff占用最少,protobuf其次,xml最后。

  1.4、生成文件大小:protostuff占用最少,protobuf其次,xml最后,前面两者是后者的1/4左右。

2、反序列化

  2.1、速度上:在反序列化对象数量较少的情况下,protobuf比protostuff快1/4左右,比xml快10+倍。但随着对象数量的增加,protobuf发生了速率明显变慢的情况!从而被protostuff赶超。

  2.2、CPU上:protostuff占用最少,protobuf其次,xml最后。

  2.3、内存上:protostuff占用最少,protobuf其次,xml最后。

3、总结

  在各个方面上,protostuff的优势非常面试,而protobuf也不弱,考虑用来代替xml。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏一个会写诗的程序员的博客

JVM、Java编译器和Java解释器

java解释器就是把在java虚拟机上运行的目标代码(字节码)解释成为具体平台的机器码的程序。即jdk或jre目录下bin目录中的java.exe文件,而jav...

2182
来自专栏互联网大杂烩

设计模式(一)

确保某一个类只有一个实例,并且自行实例化并向整个系统提供这个实例。 优点:减少系统开销,避免对资源多重利用。 缺点:没有接口,不利于扩展。

782
来自专栏向治洪

数据结构之堆和栈

内存分配策略     按照编译原理的观点,程序运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的.  静态存储分配是指在编译时就能确定每个数据目标...

1969
来自专栏机器学习算法与理论

QT生成动态链接库.so文件

首先是新建一个工程,选择C++库,打开后是这样的4 ? 然后输入新建的名称,下一步,记得勾选Qtcore那个(就是默认的那个),否则编译似乎不能通过。因为后面自...

5899
来自专栏微信公众号:Java团长

什么是线程安全,你真的了解吗?

我们整天说线程安全,但是你对什么是线程安全真的了解嘛?说真的,我之前真的是了解甚微,那么我们今天就来聊聊这个问题。

1203
来自专栏佳爷的后花媛

路由&模块化设计&命名空间

ThinkPHP采用模块化的架构思想,可以支持多模块应用的创建,让应用的扩展更加方便. 先简单说下路由规则:

933
来自专栏静默虚空的博客

Java中的访问控制权限

简介 Java中为什么要设计访问权限控制机制呢?主要作用有两点: (1)为了使用户不要触碰那些他们不该触碰的部分,这些部分对于类内部的操作时必要的,但是它并不属...

1819
来自专栏自动化测试实战

flask第十一篇——自定义url转换器

3156
来自专栏微信公众号:Java团长

《深入理解Java虚拟机》笔记

也就是说,我们完全可以做一个工具,从一个文件中读入指令,然后将这些指令运行起来。上面代码中“编好的机器指令”当然指的是能在CPU上运行的,如果这里我还实现了一个...

481
来自专栏Java编程技术

Dubbo剖析-增强SPI的实现

在Duboo剖析-整体架构分析中介绍了dubbo中除了Service 和 Config 层为 API外,其他各层均为SPI,为SPI意味着下面各层都是组件化可以...

1161

扫码关注云+社区