上一篇文章 通訊協議序列化解讀(一):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特征
-  支持protostuff-compiler產生的消息 
-  支持現有的POJO 
-  支持現有的protoc產生的Java消息 
-  與各種移動平台的互操作能力(Android、Kindle、j2me) 
-  支持轉碼 
三、工具類實現
導包:
<!-- //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。
