几种Java常用序列化框架的选型与对比

简介: 序列化与反序列化是我们日常数据持久化和网络传输中经常使用的技术,但是目前各种序列化框架让人眼花缭乱,不清楚什么场景到底采用哪种序列化框架。本文会将业界开源的序列化框架进行对比测试,分别从通用性、易用性、可扩展性、性能和数据类型与Java语法支持五方面给出对比测试。

 

image.png

 

作者 | 云烨
来源 | 阿里技术公众号

一 背景介绍

序列化与反序列化是我们日常数据持久化和网络传输中经常使用的技术,但是目前各种序列化框架让人眼花缭乱,不清楚什么场景到底采用哪种序列化框架。本文会将业界开源的序列化框架进行对比测试,分别从通用性、易用性、可扩展性、性能和数据类型与Java语法支持五方面给出对比测试。

  • 通用性:通用性是指序列化框架是否支持跨语言、跨平台。
  • 易用性:易用性是指序列化框架是否便于使用、调试,会影响开发效率。
  • 可扩展性:随着业务的发展,传输实体可能会发生变化,但是旧实体有可能还会被使用。这时候就需要考虑所选择的序列化框架是否具有良好的扩展性。
  • 性能:序列化性能主要包括时间开销和空间开销。序列化的数据通常用于持久化或网络传输,所以其大小是一个重要的指标。而编解码时间同样是影响序列化协议选择的重要指标,因为如今的系统都在追求高性能。
  • Java数据类型和语法支持:不同序列化框架所能够支持的数据类型以及语法结构是不同的。这里我们要对Java的数据类型和语法特性进行测试,来看看不同序列化框架对Java数据类型和语法结构的支持度。

下面分别对JDK Serializable、FST、Kryo、Protobuf、Thrift、Hession和Avro进行对比测试。

二 序列化框架

1 JDK Serializable

JDK Serializable是Java自带的序列化框架,我们只需要实现java.io.Serializablejava.io.Externalizable接口,就可以使用Java自带的序列化机制。实现序列化接口只是表示该类能够被序列化/反序列化,我们还需要借助I/O操作的ObjectInputStream和ObjectOutputStream对对象进行序列化和反序列化。

下面是使用JDK 序列化框架进行编解码的Demo:

 

image.png

 

通用性

由于是Java内置序列化框架,所以本身是不支持跨语言序列化与反序列化。

易用性

作为Java内置序列化框架,无序引用任何外部依赖即可完成序列化任务。但是JDK Serializable在使用上相比开源框架难用许多,可以看到上面的编解码使用非常生硬,需要借助ByteArrayOutputStream和ByteArrayInputStream才可以完整字节的转换。

可扩展性

JDK Serializable中通过serialVersionUID控制序列化类的版本,如果序列化与反序列化版本不一致,则会抛出java.io.InvalidClassException异常信息,提示序列化与反序列化SUID不一致。

java.io.InvalidClassException: com.yjz.serialization.java.UserInfo; local class incompatible: stream classdesc serialVersionUID = -5548195544707231683, local class serialVersionUID = -5194320341014913710

上面这种情况,是由于我们没有定义serialVersionUID,而是由JDK自动hash生成的,所以序列化与反序列化前后结果不一致。

但是我们可以通过自定义serialVersionUID方式来规避掉这种情况(序列化前后都是使用定义的serialVersionUID),这样JDK Serializable就可以支持字段扩展了。

private static final long serialVersionUID = 1L;

性能

JDK Serializable是Java自带的序列化框架,但是在性能上其实一点不像亲生的。下面测试用例是我们贯穿全文的一个测试实体。

public class MessageInfo implements Serializable {

    private String username;
    private String password;
    private int age;
    private HashMap<String,Object> params;
    ...
    public static MessageInfo buildMessage() {
        MessageInfo messageInfo = new MessageInfo();
        messageInfo.setUsername("abcdefg");
        messageInfo.setPassword("123456789");
        messageInfo.setAge(27);
        Map<String,Object> map = new HashMap<>();
        for(int i = 0; i< 20; i++) {
            map.put(String.valueOf(i),"a");
        }
        return messageInfo;
    }
}

使用JDK序列化后字节大小为:432。光看这组数字也许不会感觉到什么,之后我们会拿这个数据和其它序列化框架进行对比。

我们对该测试用例进行1000万次序列化,然后计算时间总和:

1000万序列化耗时(ms) 1000万反序列化耗时(ms)
38952 96508

同样我们之后会同其它序列化框架进行对比。

数据类型和语法结构支持性

由于JDK Serializable是Java语法原生序列化框架,所以基本都能够支持Java数据类型和语法。

 

image.png

 

WeakHashMap没有实现Serializable接口。

 

image.png

 

注1:但我们要序列化下面代码:

Runnable runnable = () -> System.out.println("Hello");

直接序列化会得到以下异常:

com.yjz.serialization.SerializerFunctionTest$$Lambda$1/189568618

原因就是我们Runnable的Lambda并没有实现Serializable接口。我们可以做如下修改,即可支持Lambda表达式序列化。

Runnable runnable = (Runnable & Serializable) () -> System.out.pr
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值