一些常用Java序列化框架的比较

概念

序列化:将Java对象转化为字节数组

反序列化:将字节数组转化为Java对象

在RPC应用中,进行跨进程远程调用的时候,需要使用特定的序列化技术,需要对进行网络传输的对象进行序列化和反序列化。

影响序列化选择有两个因素

1. 序列化之后码流的大小,如果太大,那么将会影响网络传输的性能。

2.     序列化和反序列化过程的性能

 

常用的序列化框架性能比较

 

 

本文主要进行以下序列化框架的对比测试:

  • 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的码流最小,序列化性能最好。

 

posted @ 2019-04-18 17:40  冬眠的山谷  阅读(8671)  评论(0编辑  收藏  举报