当前位置:   article > 正文

Java序列化框架的比较(JDK、FastJson、Hessian、Hessian2、Protostuff)_java序列化框架比较

java序列化框架比较
概念:

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

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

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

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

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

常用的序列化框架性能比较
在这里插入图片描述

序列化框架对比测试:
  • JDK
  • FastJson
  • Hessian
  • Hessian2
  • Protostuff
准备

需要序列化的对象,这是一个复杂的对象。

NettyMessage
  • 1
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 enum MessageType {
    APP_RESPONE_TYPE;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

创建一个构造器创建该对象。

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;
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

定义序列化接口

public abstract class AbstractSerialize {
    public  abstract   <T> byte[] serialize(T obj);
    public abstract  <T> T deserialize(byte[] data, Class<T> clazz);
}
  • 1
  • 2
  • 3
  • 4

JDK

实现

import lombok.SneakyThrows;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class JdkSerializeUtil extends AbstractSerialize {

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

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

        oos.writeObject(obj);
        return bos.toByteArray();

    }

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

        ObjectInputStream ois = new ObjectInputStream(bis);
        T obj = (T) ois.readObject();
        return obj;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

FastJson

引入pom

 <dependency>
     <groupId>com.alibaba</groupId>
     <artifactId>fastjson</artifactId>
     <version>1.2.56</version>
 </dependency>
  • 1
  • 2
  • 3
  • 4
  • 5

实现

import com.alibaba.fastjson.JSON;

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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

Hessian

<dependency>
    <groupId>com.caucho</groupId>
    <artifactId>hessian</artifactId>
    <version>4.0.60</version>
 </dependency>
  • 1
  • 2
  • 3
  • 4
  • 5

实现

import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;
import lombok.SneakyThrows;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

public class HessianSerializeUtil extends AbstractSerialize {

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

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        HessianOutput ho = new HessianOutput(bos);
        ho.writeObject(obj);
        return  bos.toByteArray();

    }

    @SneakyThrows
    public <T> T deserialize(byte[] data, Class<T> clazz) {
        if (data == null){
            throw new NullPointerException();
        }

        ByteArrayInputStream bis = new ByteArrayInputStream(data);
        HessianInput hi = new HessianInput(bis);
        return (T)hi.readObject();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

Hessian2

实现:

import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import lombok.SneakyThrows;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

public class Hessian2SerializeUtil extends AbstractSerialize {

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

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        Hessian2Output ho = new Hessian2Output(bos);
        ho.writeObject(obj);
        ho.flush();
        return  bos.toByteArray();

    }

    @SneakyThrows
    public <T> T deserialize(byte[] data, Class<T> clazz) {
        if (data == null){
            throw new NullPointerException();
        }

        ByteArrayInputStream bis = new ByteArrayInputStream(data);
        Hessian2Input hi = new Hessian2Input(bis);
        return (T)hi.readObject();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

实现:

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;
    }


}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

测试

测试方法

import com.lot.entity.NettyMessage;
import com.lot.entity.NettyMessageBuilder;
import com.lot.serialize.AbstractSerialize;
import com.lot.serialize.FastjsonSerializeUtil;
import com.lot.serialize.HessianSerializeUtil;
import com.lot.serialize.JdkSerializeUtil;
import com.lot.serialize.ProtostuffSerializeUtil;

public class Main {

    public static void main(String[] args) {
        //这里替换各种序列化实现类
//        AbstractSerialize serialize = new JdkSerializeUtil();
//        AbstractSerialize serialize = new FastjsonSerializeUtil();
//        AbstractSerialize serialize = new HessianSerializeUtil();
        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);

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

这里定义了一个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;
    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
码流大小(byte)10次(us)100次(us)1000次(us)10000次(us)100000次(us)
FastJson305116-243106-18590-14026-398-12
JDK866383-777502-1101123-33454-23715-76
Hessian520959-3836376-567191-32999-16130-47
Hessian2398132-152142-14433-3512-136-6
Protostuff193103-14590-13775-13515-245-8

注:

  1. 码流单位为字节
  2. 序列化耗时-反序列化耗时,单位为微秒

从以上测试可以看出

  1. JDK方式的码流最大,不利于网络传输。
  2. 从整体来看,Prorostuff的码流最小,Prorostuff 和 Hessian2序列化性能最好。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/一键难忘520/article/detail/1012013
推荐阅读
相关标签
  

闽ICP备14008679号