一些經常使用Java序列化框架的比較

概念

序列化:將Java對象轉化爲字節數組java

反序列化:將字節數組轉化爲Java對象json

在RPC應用中,進行跨進程遠程調用的時候,須要使用特定的序列化技術,須要對進行網絡傳輸的對象進行序列化和反序列化。數組

影響序列化選擇有兩個因素緩存

1. 序列化以後碼流的大小,若是太大,那麼將會影響網絡傳輸的性能。安全

2.     序列化和反序列化過程的性能網絡

 

經常使用的序列化框架性能比較session

 

 

本文主要進行如下序列化框架的對比測試:框架

  • JDK
  • FastJson
  • Hessian
  • Protostuff

準備

須要序列化的對象,這是一個複雜的對象。性能

NettyMessage
public class NettyMessage  implements Serializable {

    //消息頭
    private Header header;
    //消息體
    private Object body;
}

@Data
public class Header implements Serializable {

    //校驗頭
    private int crcCode;

    //消息頭消息體的總長度
    private  int length;

    //全局惟一id
    private  long sessionId;

    //消息類型
    private  MessageType type;

    //擴展字段
    private Map<String,Object> attachment;
}

@Data
public class RpcRequest implements Serializable {
    private long requestId;  //請求id
    private String interfaceName;  //調用類名
    private String methodName; //調用方法名
    private String[] parameterTypes; //方法參數類型
    private Object[] parameters;   //方法參數


}

 

 

建立一個構造器建立該對象。測試

public class NettyMessageBuilder {

    public  static NettyMessage build(){

        NettyMessage message = new NettyMessage();
        Header header = new Header();
        RpcRequest request = new RpcRequest();

        header.setCrcCode(1234);
        header.setType(MessageType.APP_RESPONE_TYPE);
        header.setLength(100);
        header.setSessionId(200);

        Map<String,Object> map = new LinkedHashMap<>();

        map.put("demoKey",(Object)"demoValue");
        header.setAttachment(map);


        request.setInterfaceName("com.demo");
        String[] types = {"java.lang.String" ,"java.lang.Integer"};
        String[] param = {"java.lang.String" ,"java.lang.Integer"};
        request.setParameterTypes(types);
        request.setParameters(param);
        request.setMethodName("buy");
        request.setRequestId(123456);


        message.setHeader(header);
        message.setBody(request);

        return  message;
    }

}

 

定義序列化接口

public abstract class AbstractSerialize {

    public  abstract   <T> byte[] serialize(T obj);
    public abstract  <T> T deserialize(byte[] data, Class<T> clazz);


}

 

JDK

實現

public class JdkSerializeUtil extends AbstractSerialize {

    public <T> byte[] serialize(T obj) {

        if (obj  == null){
            throw new NullPointerException();
        }

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(bos);

            oos.writeObject(obj);
            return bos.toByteArray();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return new byte[0];
    }

    public <T> T deserialize(byte[] data, Class<T> clazz) {
        ByteArrayInputStream bis = new ByteArrayInputStream(data);

        try {
            ObjectInputStream ois = new ObjectInputStream(bis);
            T obj = (T)ois.readObject();
            return obj;
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return  null;
    }
}

 

 

FastJson

引入pom

 <dependency>
     <groupId>com.alibaba</groupId>
     <artifactId>fastjson</artifactId>
     <version>1.2.56</version>
 </dependency>
            

 

實現

public class FastjsonSerializeUtil  extends AbstractSerialize {

    public <T> byte[] serialize(T obj) {
        if (obj  == null){
            throw new NullPointerException();
        }

        String json = JSON.toJSONString(obj);
        byte[] data = json.getBytes();
        return data;
    }

    public <T> T deserialize(byte[] data, Class<T> clazz) {

        T obj = JSON.parseObject(new String(data),clazz);
        return obj;
    }
}

 

Hessian

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

 

實現

@Slf4j
public class HessianSerializeUtil extends AbstractSerialize {



    public <T> byte[] serialize(T obj) {

        if (obj  == null){
            throw new NullPointerException();
        }
        try{
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            HessianOutput ho = new HessianOutput(bos);
            ho.writeObject(obj);

            return  bos.toByteArray();
        }
        catch(Exception ex){
            log.error("HessianSerializeUtil序列化發生異常!"+ex);
            throw new  RuntimeException();
        }

    }

    public <T> T deserialize(byte[] data, Class<T> clazz) {

        if (data == null){
            throw  new  NullPointerException();
        }
        try{
            ByteArrayInputStream bis = new ByteArrayInputStream(data);
            HessianInput hi = new HessianInput(bis);
            return (T)hi.readObject();

        }
        catch(Exception ex){
            log.error("HessianSerializeUtil反序列化發生異常!"+ex);
            throw new  RuntimeException();
        }

    }
}

 

 

Protostuff

<dependency>
    <groupId>io.protostuff</groupId>
    <artifactId>protostuff-core</artifactId>
    <version>1.6.0</version>
     <scope>compile</scope>
</dependency>


<!-- https://mvnrepository.com/artifact/io.protostuff/protostuff-runtime -->
 <dependency>
    <groupId>io.protostuff</groupId>
    <artifactId>protostuff-runtime</artifactId>
    <version>1.6.0</version>
</dependency>

 

實現

public class ProtostuffSerializeUtil  extends AbstractSerialize {

    /**
     * 避免每次序列化都從新申請Buffer空間
     */
    private static LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
    /**
     * 緩存Schema
     */
    private static Map<Class<?>, Schema<?>> schemaCache = new ConcurrentHashMap<Class<?>, Schema<?>>();

    public   <T> byte[] serialize(T obj) {

        if (obj  == null){
            throw new NullPointerException();
        }
        Class<T> clazz = (Class<T>) obj.getClass();
        Schema<T> schema = getSchema(clazz);
        byte[] data;
        try {
            data = ProtostuffIOUtil.toByteArray(obj, schema, buffer);
        } finally {
            buffer.clear();
        }

        return data;
    }

    public <T> T deserialize(byte[] data, Class<T> clazz) {
        Schema<T> schema = getSchema(clazz);
        T obj = schema.newMessage();
        ProtostuffIOUtil.mergeFrom(data, obj, schema);
        return obj;
    }


    private static <T> Schema<T> getSchema(Class<T> clazz) {
        Schema<T> schema = (Schema<T>) schemaCache.get(clazz);
        if (schema == null) {
            //這個schema經過RuntimeSchema進行懶建立並緩存
            //因此能夠一直調用RuntimeSchema.getSchema(),這個方法是線程安全的
            schema = RuntimeSchema.getSchema(clazz);
            if (schema != null) {
                schemaCache.put(clazz, schema);
            }
        }

        return schema;
    }


}

 

 

測試

測試方法

 @Test
    public void testFastJsonSerialize(){

     //這裏替換各類序列化實現類 AbstractSerialize serialize
= new ProtostuffSerializeUtil(); NettyMessage message = NettyMessageBuilder.build(); TimeUtil timeUtil = new TimeUtil(); TimeUtil timeUtil1 = new TimeUtil(); NettyMessage result = null; byte[] serByte = serialize.serialize(message); System.out.println("字節長度:" + serByte.length); result = serialize.deserialize(serByte,NettyMessage.class);
     //這裏設置測試次數
for(int i = 0; i< 100000; i++){ //timeUtil.init(); timeUtil.start(); serByte = serialize.serialize(message); timeUtil.end(); //System.out.println("序列化時間:"+ timeUtil.getAvrTimeUs() + " Us"); timeUtil1.start(); result = serialize.deserialize(serByte,NettyMessage.class); timeUtil1.end(); } System.out.println("序列化時間:"+ timeUtil.getAvrTimeUs() + " Us"); System.out.println("反序列化時間:"+ timeUtil1.getAvrTimeUs() + " Us"); System.out.println("結果:" + result); }

 

這裏定義了一個TimeUtil類來計時

 

public class TimeUtil {

    private  long startTime;
    private  long endTime;
    private  long timeSum;
    private  long count;

    public  void init(){
        timeSum = 0;
        count = 0;
    }

    public    void start(){
        startTime = System.nanoTime();

    }

    public  void end(){
        endTime = System.nanoTime();
        timeSum += (endTime-startTime);
        count++;
    }

    public   long getAvrTimeNs(){
        return (timeSum/count);
    }
    public   long getAvrTimeUs(){
        return (timeSum/count)/1000;
    }

    public   long getAvrTimeMs(){
        return (timeSum/count)/1000000;
    }
    
}

 

 

  碼流大小(byte) 10次(us) 100次(us) 1000次(us) 10000次(us) 100000次(us)  
FastJson 305 116-243 106-185 90-140 26-39 8-12  
JDK 866 383-777 502-1101 123-334 54-237 15-76  
Hessian 520 959-3836 376-567 191-329 99-161 30-47  
Protostuff 193 103-145 90-137 75-135 15-24 5-8  
               

 

 

 

 

 

 

 

 

 

注:

1. 碼流單位爲字節

2. 序列化耗時-反序列化耗時,單位爲微秒

從以上測試能夠看出

1. JDK方式的碼流最大,不利於網絡傳輸。

2. 從總體來看,Prorostuff的碼流最小,序列化性能最好。

相關文章
相關標籤/搜索