序列化方式介绍和性能比较 (kryo fastjson hessian jdk)

目录

测试条件

序列化对象

结果展示

码流大小比较

fastJson < kryo < hessian < jdk

序列化+反序列化速度

测试代码

序列化+反序列化

序列化速度

反序列化速度

待验证

四种序列化介绍以及Demo

JDK自带序列化

Hessian

Hessian2.0的Demo实现

Kryo

为什么kryo序列化后的字节流很小?

FastJson


测试条件

  • 操作系统:Windows
  • 内存:16 GB DDR4
  • 处理器:3.2GHz 八核 AMD Ryzen 7 5800H

序列化框架版本

序列化框架 fastjson kryo hessian jdk
版本 1.2.29 3.0.3 4.0.62 1.8

序列化对象

采用同一个对象 RpcInvocation 进行测试,由于 bean 对象的不同,测试结果可能会有差异。

 static final String UUIDstr = UUID.randomUUID().toString();
    static RpcInvocation rpcInvocation = new RpcInvocation();

    static {
        rpcInvocation.setRetry(1);
        rpcInvocation.setTargetMethod("sendData");
        rpcInvocation.setTargetServiceName("org.idea.VVrpc.framework.interfaces.DataService");
        rpcInvocation.setArgs(new Object[]{"hello"});
        rpcInvocation.setUuid(UUIDstr);
    }

结果展示

码流大小比较

序列化方式介绍和性能比较 (kryo fastjson hessian jdk)_第1张图片

fastJson < kryo < hessian < jdk

序列化+反序列化速度

测试代码


@BenchmarkMode({Mode.Throughput})
@Warmup(iterations = 3,time = 3,timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 3, time = 5,timeUnit = TimeUnit.SECONDS)
@Fork(1)
public class SerializeRpcCompareTest {

    static final String UUIDstr = UUID.randomUUID().toString();
    static RpcInvocation rpcInvocation = new RpcInvocation();

    static {
        rpcInvocation.setRetry(1);
        rpcInvocation.setTargetMethod("sendData");
        rpcInvocation.setTargetServiceName("org.idea.VVrpc.framework.interfaces.DataService");
        rpcInvocation.setArgs(new Object[]{"hello"});
        rpcInvocation.setUuid(UUIDstr);
    }

    static SerializeFactory jdkserializeFactory = new JdkSerializeFactory();
    static SerializeFactory hesserializeFactory = new HessianSerializeFactory();
    static SerializeFactory fastserializeFactory = new FastJsonSerializeFactory();
    static SerializeFactory kryoserializeFactory = new KryoSerializeFactory();

    @Benchmark
    public void jdkSerializeTest(){
        byte[] result = jdkserializeFactory.serialize(rpcInvocation);
        RpcInvocation deserializeUser = jdkserializeFactory.deserialize(result,RpcInvocation.class);
    }

    @Benchmark
    public void hessianSerializeTest(){
        byte[] result = hesserializeFactory.serialize(rpcInvocation);
        RpcInvocation deserializeUser = hesserializeFactory.deserialize(result,RpcInvocation.class);
    }

    @Benchmark
    public void fastJsonSerializeTest(){
        byte[] result = fastserializeFactory.serialize(rpcInvocation);
        RpcInvocation deserializeUser = fastserializeFactory.deserialize(result,RpcInvocation.class);
    }

    @Benchmark
    public void kryoSerializeTest(){
        byte[] result = kryoserializeFactory.serialize(rpcInvocation);
        RpcInvocation deserializeUser = kryoserializeFactory.deserialize(result,RpcInvocation.class);
    }

    public static void main(String[] args) throws RunnerException {
        Options options = new OptionsBuilder()
                .include(SerializeRpcCompareTest.class.getSimpleName())
                .build();
        new Runner(options).run();
    }
}

序列化+反序列化

Score 表示 每秒进行 score次操作   单位: ops: operations per second

一次操作指的是 执行一次方法体内的程序。

序列化方式介绍和性能比较 (kryo fastjson hessian jdk)_第2张图片

序列化速度

 序列化方式介绍和性能比较 (kryo fastjson hessian jdk)_第3张图片

反序列化速度

序列化方式介绍和性能比较 (kryo fastjson hessian jdk)_第4张图片 

fastjson >  kryo > hessian >> jdk

网上的测试 kryo大都排在第一位,可能测试的代码或者场景有些问题。

搜到的一些可能原因:

1.kryo 针对每一种类型 在序列化反序列化前都要实例化,否则会异常
但是实例化又会影响性能,所以使用时,针对特定类型只要实例化一次,速度就会飞快.

2.kryo在复杂对象的序列化时效果会比较好。

序列化方式介绍和性能比较 (kryo fastjson hessian jdk)_第5张图片

待验证

四种序列化介绍以及Demo

JDK自带序列化

JDK自带的序列化框架使用_trigger333的博客-CSDN博客

深入理解Java序列化机制:ObjectOutputStream源码简要分析_A__Plus的博客-CSDN博客_objectoutputstream

Hessian

Hessian是caucho公司开发的轻量级RPC(Remote Procedure Call)框架,它使用HTTP协议传输,使用Hessian二进制序列化。

Hessian由于其支持跨语言、高效的二进制序列化协议,被经常用于序列化框架使用。Hessian序列化协议分为Hessian1.0和Hessian2.0,Hessian2.0协议对序列化过程进行了优化(优化内容待看),在性能上相较Hessian1.0有明显提升。

Hessian2.0的Demo实现

public class HessianSerializeFactory implements SerializeFactory {

    @Override
    public  byte[] serialize(T t) {
        byte[] data = null;
        try {
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            Hessian2Output output = new Hessian2Output(os);
            output.writeObject(t);
            output.getBytesOutputStream().flush();
            output.completeMessage();
            output.close();
            data = os.toByteArray();
        } catch (Exception e) {
           throw new RuntimeException(e);
        }
        return data;
    }

    @Override
    public  T deserialize(byte[] data, Class clazz) {
        if (data == null) {
            return null;
        }
        Object result = null;
        try {
            ByteArrayInputStream is = new ByteArrayInputStream(data);
            Hessian2Input input = new Hessian2Input(is);
            result = input.readObject();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return (T) result;
    }

Kryo

Kryo一个快速有效的Java二进制序列化框架,它依赖底层ASM库用于字节码生成,因此有比较好的运行速度。Kryo的目标就是提供一个序列化速度快、结果体积小、API简单易用的序列化框架。Kryo支持自动深/浅拷贝,它是直接通过对象->对象的深度拷贝,而不是对象->字节->对象的过程。
kryo不是线程安全的, 所以需要使用ThreadLocal来存储kryo对象

比kyro更高效的序列化库就只有google的protobuf了(而且两者性能很接近),protobuf有个缺点就是要传输的每一个类的结构都要生成对应的proto文件(也可以都放在同一个proto文件中,如果考虑到扩展性的话,不建议放在一个proto文件中),如果某个类发生修改,还得重新生成该类对应的proto文件.

为什么kryo序列化后的字节流很小?

源码分析Dubbo序列化-源码分析kryo序列化实现原理 - 码农岛

Kryo 对 Class 的序列化只需要化 Class 的全路径名,在反序列化时根据 Class 通过类加载进行加载,大大减少了序列化后的文件大小,能极大提高性能。

Kryo 的核心设计理念就是尽最大可能减少序列化后的文件大小,其举措1就是通过对long,int等数据类型,采用变长字节存储来代替java中使用固定字节(4,8)字节的模式,因为在软件开发中,对象的这些值基本上都是小值,能节省很多空间,第二个举措是使用了类似缓存的机制,在一次序列化对象中,在整个递归序列化期间,相同的对象,只会序列化一次,后续的用一个局部int值来代替。

public class KryoSerializeFactory implements SerializeFactory {

    private static final ThreadLocal kryos = new ThreadLocal() {
        @Override
        protected Kryo initialValue() {
            Kryo kryo = new Kryo();
            return kryo;
        }
    };

    @Override
    public  byte[] serialize(T t) {
        Output output = null;
        try {
            // 从当前线程中的 ThreadLocalMap 中拿到 kryo 并写入字节流 
            Kryo kryo = kryos.get();
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            output = new Output(byteArrayOutputStream);
            kryo.writeClassAndObject(output, t);
            return output.toBytes();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (output != null) {
                output.close();
            }
        }
    }

    @Override
    public  T deserialize(byte[] data, Class clazz) {
        Input input = null;
        try {
            // 从当前线程中的 ThreadLocalMap 中拿到 kryo 并将字节流转化为 对象
            Kryo kryo = kryos.get();
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data);
            input = new Input(byteArrayInputStream);
            return (T) kryo.readClassAndObject(input);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (input != null) {
                input.close();
            }
        }
    }

}

 

FastJson

fastjson详解_吴声子夜歌的博客-CSDN博客_fastjson

​在前后端数据传输交互中,经常会遇到字符串(String)与json,XML等格式相互转换与解析,其中json以跨语言,跨前后端的优点在开发中被频繁使用,基本上可以说是标准的数据交换格式。fastjson 是一个java语言编写的高性能且功能完善的JSON库,它采用一种“假定有序快速匹配”的算法,把JSON Parse 的性能提升到了极致。它的接口简单易用,已经被广泛使用在缓存序列化,协议交互,Web输出等各种应用场景中。
Demo 把 json 字符串转为 byte数组即可。

public class FastJsonSerializeFactory implements SerializeFactory {

    @Override
    public  byte[] serialize(T t) {
        String jsonStr = JSON.toJSONString(t);
        return jsonStr.getBytes();
    }

    @Override
    public  T deserialize(byte[] data, Class clazz) {
        return JSON.parseObject(new String(data),clazz);
    }

}

转换效果:

 

参考:

几种常用序列化框架_涛歌依旧fly的博客-CSDN博客_序列化框架

jackson、fastjson、kryo、protostuff等序列化工具性能对比_双面神像的博客-CSDN博客_kryo和fastjson

你可能感兴趣的:(RPC,servlet,java,spring)