转载-七种Java常用序列化框架的选型与对比

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

本文章转自:乐字节

文章主要讲解:Java常用序列化框架

获取更多前端相关资料可以点击链接加入群聊【Java技术交流群】:正在跳转暗号:166

转载地址:七种Java常用序列化框架的选型与对比 - 知乎

一 背景介绍

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

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

二 序列化框架

1 JDK Serializable

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

通用性

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

易用性

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

可扩展性

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

性能

JDK Serializable是Java自带的序列化框架,但是在性能上其实一点不像亲生的。下面测试用例是我们贯穿全文的一个测试实体。
我们对该测试用例进行1000万次序列化,然后计算时间总和:

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

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

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

WeakHashMap没有实现Serializable接口。

2 FST序列化框架

FST(fast-serialization)是完全兼容JDK序列化协议的Java序列化框架,它在序列化速度上能达到JDK的10倍,序列化结果只有JDK的1/3。目前FST的版本为2.56,在2.17版本之后提供了对Android的支持。

通用性

FST同样是针对Java而开发的序列化框架,所以也不存在跨语言特性。

易用性

在易用性上,FST可以说能够甩JDK Serializable几条街,语法极其简洁,FSTConfiguration封装了大部分方法。

可扩展性

FST通过@Version注解能够支持新增字段与旧的数据流兼容。对于新增的字段都需要通过@Version注解标识,没有版本注释意味着版本为0。

注意:

综合来看,FST在扩展性上面虽然支持,但是用起来还是比较繁琐的。

性能

使用FST序列化上面的测试用例,序列化后大小为:172,相比JDK序列化的432 ,将近减少了1/3。下面我们再看序列化与反序列化的时间开销。

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

FST是基于JDK序列化框架而进行开发的,所以在数据类型和语法上和Java支持性一致。

3 Kryo序列化框架

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

通用性

首先Kryo官网说自己是一款Java二进制序列化框架,其次在网上搜了一遍没有看到Kryo的跨语言使用,只是一些文章提及了跨语言使用非常复杂,但是没有找到其它语言的相关实现。

易用性

在使用方式上Kryo提供的API也是非常简洁易用,Input和Output封装了你几乎能够想到的所有流操作。Kryo提供了丰富的灵活配置,比如自定义序列化器、设置默认序列化器等等,这些配置使用起来还是比较费劲的。

可扩展性

Kryo默认序列化器FiledSerializer是不支持字段扩展的,如果想要使用扩展序列化器则需要配置其它默认序列化器。

性能

使用Kryo测试上面的测试用例,Kryo序列化后的字节大小为172 ,和FST未经优化的大小一致。时间开销如下:

我们同样关闭循环引用配置和预注册序列化类,序列化后的字节大小为120,因为这时候类序列化的标识是使用的数字,而不是类全名。使用的是时间开销如下:

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

Kryo对于序列化类的基本要求就是需要含有无参构造函数,因为反序列化过程中需要使用无参构造函数创建对象。

4 Protocol buffer

Protocol buffer是一种语言中立、平台无关、可扩展的序列化框架。Protocol buffer相较于前面几种序列化框架而言,它是需要预先定义Schema的。

通用性

protobuf设计之初的目标就是能够设计一款与语言无关的序列化框架,它目前支持了Java、Python、C++、Go、C#等,并且很多其它语言都提供了第三方包。所以在通用性上,protobuf是非常给力的。

易用性

protobuf需要使用IDL来定义Schema描述文件,定义完描述文件后,我们可以直接使用protoc来直接生成序列化与反序列化代码。所以,在使用上只需要简单编写描述文件,就可以使用protobuf了。

可扩展性

可扩展性同样是protobuf设计之初的目标之一,我们可以非常轻松的在.proto文件进行修改。
新增字段:对于新增字段,我们一定要保证新增字段要有对应的默认值,这样才能够与旧代码交互。相应的新协议生成的消息,可以被旧协议解析。
删除字段:删除字段需要注意的是,对应的字段、标签不能够在后续更新中使用。为了避免错误,我们可以通过reserved规避带哦。

protobuf在数据兼容性上也非常友好,int32、unit32、int64、unit64、bool是完全兼容的,所以我们可以根据需要修改其类型。
通过上面来看,protobuf在扩展性上做了很多,能够很友好的支持协议扩展。

性能

我们同样使用上面的实例来进行性能测试,使用protobuf序列化后的字节大小为 192,下面是对应的时间开销。

可以看出protobuf的反序列化性能要比FST、Kryo差一些。

数据类型和语法结构支持

Protobuf使用IDL定义Schema所以不支持定义Java方法,下面序列化变量的测试:

注:List、Set、Queue通过protobuf repeated定义测试的。只要实现Iterable接口的类都可以使用repeated列表。

5 Thrift序列化框架

Thrift是由Facebook实现的一种高效的、支持多种语言的远程服务调用框架,即RPC(Remote Procedure Call)。后来Facebook将Thrift开源到Apache。可以看到Thrift是一个RPC框架,但是由于Thrift提供了多语言之间的RPC服务,所以很多时候被用于序列化中。

使用Thrift实现序列化主要分为三步,创建thrift IDL文件、编译生成Java代码、使用TSerializer和TDeserializer进行序列化和反序列化。

通用性

Thrift和protobuf类似,都需要使用IDL定义描述文件,这是目前实现跨语言序列化/RPC的一种有效方式。Thrift目前支持 C++、Java、Python、PHP、Ruby、 Erlang、Perl、Haskell、C#、Cocoa、JavaScript、Node.js、Smalltalk、OCaml、Delphi等语言,所以可以看到Thrift具有很强的通用性。

易用性

Thrift在易用性上和protobuf类似,都需要经过三步:使用IDL编写thrift文件、编译生成Java代码和调用序列化与反序列化方法。protobuf在生成类中已经内置了序列化与反序列化方法,而Thrift需要单独调用内置序列化器来进行编解码。

可扩展性

Thrift支持字段扩展,在扩展字段过程中需要注意以下问题:

性能

上面的测试用例,使用Thrift序列化后的字节大小为:257,下面是对应的序列化时间与反序列化时间开销:

Thrift在序列化和反序列化的时间开销总和上和protobuf差不多,protobuf在序列化时间上更占优势,而Thrift在反序列化上有自己的优势。

数据类型和语法结构支持

数据类型支持:由于Thrift使用IDL来定义序列化类,所以能够支持的数据类型就是Thrift数据类型。Thrift所能够支持的Java数据类型:

Thrift同样不支持定义Java方法。

6 Hessian序列化框架

Hessian是caucho公司开发的轻量级RPC(Remote Procedure Call)框架,它使用HTTP协议传输,使用Hessian二进制序列化。
Hessian由于其支持跨语言、高效的二进制序列化协议,被经常用于序列化框架使用。Hessian序列化协议分为Hessian1.0和Hessian2.0,Hessian2.0协议对序列化过程进行了优化(优化内容待看),在性能上相较Hessian1.0有明显提升。
使用Hessian序列化非常简单,只需要通过HessianInput和HessianOutput即可完成对象的序列化,下面是Hessian序列化的Demo:

通用性

Hessian与Protobuf、Thrift一样,支持跨语言RPC通信。Hessian相比其它跨语言PRC框架的一个主要优势在于,它不是采用IDL来定义数据和服务,而是通过自描述来完成服务的定义。目前Hessian已经实现了语言包括:Java、Flash/Flex、Python、C++、.Net/C#、D、Erlang、PHP、Ruby、Object-C。

易用性

相较于Protobuf和Thrift,由于Hessian不需要通过IDL来定义数据和服务,对于序列化的数据只需要实现Serializable接口即可,所以使用上相比Protobuf和Thrift更加容易。

可扩展性

Hession序列化类虽然需要实现Serializable接口,但是它并不受serialVersionUID影响,能够轻松支持字段扩展。

性能

使用Hessian1.0协议序列化上面的测试用例,序列化结果大小为277。使用Hessian2.0序列化协议,序列化结果大小为178。

序列化化与反序列化的时间开销如下:

可以看到Hessian1.0的无论在序列化后体积大小,还是在序列化、反序列化时间上都比Hessian2.0相差很远。

数据类型和语法结构支持

由于Hession使用Java自描述序列化类,所以Java原生数据类型、集合类、自定义类、枚举等基本都能够支持(SynchronousQueue不支持),Java语法结构也能够很好的支持。

7 Avro序列化框架

Avro是一个数据序列化框架。它是Apache Hadoop下的一个子项目,由Doug Cutting主导Hadoop过程中开发的数据序列化框架。Avro在设计之初就用于支持数据密集型应用,很适合远程或本地大规模数据交换和存储。

通用性

Avro通过Schema定义数据结构,目前支持Java、C、C++、C#、Python、PHP和Ruby语言,所以在这些语言之间Avro具有很好的通用性。

易用性

Avro对于动态语言无需生成代码,但对于Java这类静态语言,还是需要使用avro-tools.jar来编译生成Java代码。在Schema编写上,个人感觉相比Thrift、Protobuf更加复杂。

可扩展性

性能

使用Avro生成代码序列化之后的结果为:111。下面是使用Avro序列化的时间开销:

数据类型和语法结构支持

Avro需要使用Avro所支持的数据类型来编写Schema信息,所以能够支持的Java数据类型即为Avro所支持的数据类型。Avro支持数据类型有:基础类型(null、boolean、int、long、float、double、bytes、string),复杂数据类型(Record、Enum、Array、Map、Union、Fixed)。

Avro自动生成代码,或者直接使用Schema,不能支持在序列化类中定义java方法。

三 总结

1 通用性

下面是从通用性上对比各个序列化框架,可以看出Protobuf在通用上是最佳的,能够支持多种主流变成语言。

2 易用性

下面是从API使用的易用性上面来对比各个序列化框架,可以说除了JDK Serializer外的序列化框架都提供了不错API使用方式。

3 可扩展性

下面是各个序列化框架的可扩展性对比,可以看到Protobuf的可扩展性是最方便、自然的。其它序列化框架都需要一些配置、注解等操作。

4 性能

序列化大小对比

对比各个序列化框架序列化后的数据大小如下,可以看出kryo preregister(预先注册序列化类)和Avro序列化结果都很不错。所以,如果在序列化大小上有需求,可以选择Kryo或Avro。

序列化时间开销对比

下面是序列化与反序列化的时间开销,kryo preregister和fst preregister都能提供优异的性能,其中fst pre序列化时间就最佳,而kryo pre在序列化和反序列化时间开销上基本一致。所以,如果序列化时间是主要的考虑指标,可以选择Kryo或FST,都能提供不错的性能体验。

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

各序列化框架对Java数据类型支持的对比:

注:集合类型测试基本覆盖了所有对应的实现类。

下面根据测试总结了以上序列化框架所能支持的数据类型、语法。

由于Protobuf、Thrift是IDL定义类文件,然后使用各自的编译器生成Java代码。IDL没有提供定义staic内部类、非static内部类等语法,所以这些功能无法测试

如果有侵权请及时联系博主,谢谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/428167.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux设备模型(九) - bus/device/device_driver/class

一,设备驱动模型 1,概述 在前面写的驱动中,我们发现编写驱动有个固定的模式只有往里面套代码就可以了,它们之间的大致流程可以总结如下: 实现入口函数xxx_init()和卸载函数xxx_exit() 申请设备号 register_chrdev_r…

软件测试的基本流程是什么?软件测试流程详细介绍

软件测试和软件开发一样,是一个比较复杂的工作过程,如果无章法可循,随意进行测试势必会造成测试工作的混乱。为了使测试工作标准化、规范化,并且快速、高效、高质量地完成测试工作,需要制订完整且具体的测试流程。 01…

中国各省绿色金融试点DID数据集(2010-2023)

一、数据简介 最新的2010-2023年中国31省绿色金融试点DID数据,供大家研究使用。 数据说明:内含绿色金融改革创新试验区名单,将试验区获批当年及以后的政策虚拟变量项是否试点赋值为1,获批之前赋值为0。其中赣江新区、贵安新区为国…

Spring:FactoryBean预加载逻辑以及自定义实现Mybatis的接口扫描

Spring:FactoryBean预加载逻辑以及自定义实现Mybatis的接口扫描 1 前言 参考Mybatis框架的Mapper注解扫描Mapper接口的业务逻辑,其中集成Spring的逻辑里使用到了Spring框架的FactoryBean拓展点,本文针对Spring FactoryBean的加载流程进行分…

Unity 摄像机的深度切换与摄像机画面投影

摄像机可选:透视、正交 正交类似投影,1比1 透视类似人眼,近大远小 摄像机投影 在项目中新建:渲染器纹理 将新建纹理拖动到相机的目标纹理中 新建一个平面,将新建材质组件放到平面中即可。 相机深度切换 使用代…

Git实战(2)

git work flow ------------------------------------------------------- ---------------------------------------------------------------- 场景问题及处理 问题1:最近提交了 a,b,c,d记录,想把b记录删掉其他提交记录保留: git reset …

JavaWeb之 Web概述

目录 前言1.1 Web和 JavaWeb的概念1.2 JavaWeb技术栈1.2.1 B/S架构1.2.2 静态资源1.2.3 动态资源1.2.4 数据库1.2.5 HTTP协议1.2.6 Web服务器 1.3 JavaWeb 学习内容 前言 博主将用 CSDN 记录 Java 后端开发学习之路上的经验,并将自己整理的编程经验和知识分享出来&a…

浅谈MySQL的B树索引与索引优化

MySQL的MyISAM、InnoDB引擎默认均使用B树索引(查询时都显示为“BTREE”),本文讨论两个问题: 为什么MySQL等主流数据库选择B树的索引结构?如何基于索引结构,理解常见的MySQL索引优化思路? 为什…

S7-1500 PLC装载存储器已使用容量变红的解决方法示例

S7-1500 PLC装载存储器已使用容量变红的解决方法示例 1.如何在线查看S7-1200/1500 PLC的内部存储区的使用情况? 答:在项目树中展开PLC程序打开“在线和诊断”,点击“转至在线”使TIA PORTAL在线连接到S7-1200 CPU,在“存储器”标签查看CPU内存使用情况,如下图所示: 2.如何…

LeetCode234题:回文链表(python3)

代码思路:将链表的值复制到数组列表中,再使用双指针法判断,不断更新current_node的值。 # Definition for singly-linked list. # class ListNode: # def __init__(self, val0, nextNone): # self.val val # self.next…

探索Linux世界:初次接触和基本指令(文件操作)

文章目录 1.基本介绍和准备2.基本指令和Linux的基本操作3.几个重要基本指令3.1 ls - 列出文件和目录3.1.1文件的知识3.1.2 .和..文件 3.2pwd - 显示当前工作目录3.2.1路径知识 3.3 cd - 切换目录3.4 touch - 创建文件或更新时间戳3.5mkdir - 创建新目录3.6rm - 删除文件或目录3…

基本运算符

参考C Primer Plus进行C语言学习 文章目录 基本运算符 除法运算符:/其他运算 1、除法运算符 在C语言中,整数除法结果的小数部分被丢弃,这一过程被称为截断。 2.其他运算符 (1)sizeof运算符和size_t类型 回顾一下&…

C++ 前缀和

目录 例1 例2 例3 例4 例5 例6 例7 例8 例1 DP34 【模板】前缀和 分析:dp和arr的大小并不是固定的,就是有没有偏移量,这里的n是从1开始,不如直接放到下标1处,在最后的减法时,如果用第一个参考代码会…

单调栈的理解

单调栈的理解 核心代码场景思考 完整代码环形数组循环数组 单调栈&#xff1a; 单调递增或 单调递减的栈 核心代码 while (!s.empty()&&s.peek()<nums[i]){s.pop(); } s.push(nums[i]);将要放入的元素&#xff0c;与栈内元素依个比较&#xff0c;小于的都出栈&am…

设计模式(含7大原则)面试题

目录 主要参考文章 设计模式的目的 设计模式的七大原则 设计模式的三大分类及关键点 1、创建型模式&#xff08;用于解耦对象的实例化过程&#xff09; 2、结构型模式 3、行为型模式 23种设计模式&#xff08;乱序--现学现写&#xff0c;不全面--应付面试为主&#xff…

BUUCTF------[HCTF 2018]WarmUp

开局一个表情&#xff0c;源代码发现source.php <?phphighlight_file(__FILE__);class emmm{public static function checkFile(&$page){$whitelist ["source">"source.php","hint">"hint.php"];if (! isset($page) |…

web坦克大战小游戏

H5小游戏源码、JS开发网页小游戏开源源码大合集。无需运行环境&#xff0c;解压后浏览器直接打开。有需要的订阅后&#xff0c;私信本人&#xff0c;发源码&#xff0c;含60小游戏源码。如五子棋、象棋、植物大战僵尸、贪吃蛇、飞机大战、坦克大战、开心消消乐、扑鱼达人、扫雷…

基于小红书评论的文本词语频数挖掘和词云图

import pandas as pd df pd.read_csv(小红书评论.csv) # 读取小红书评论数据 text .join(df[内容].astype(str)).strip() # 将内容列所有数据合成字符串 print(text) 使用jieba库&#xff0c;对文本数据进行分词&#xff0c;并统计出现频数 import jieba from collectio…

JMeter Body Data模拟10000个字符串

方法 **这个表达式使用了JMeter中的Groovy函数&#xff0c;目的是生成一个包含10000个字符 "s" 的字符串。在Groovy语言中&#xff0c;使用 "s" * 10000 可以生成包含10000个 "s" 的字符串。${__groovy("s" * 10000,)} 这个表达式在J…

财报解读:基本盘稳定后,联想如何进一步抢占AI时代?

从2021年下半年开始&#xff0c;受诸多因素影响&#xff0c;消费电子行业始终处在承压状态&#xff0c;“不景气”这一关键词屡次被市场提及。 但寒气没有持续&#xff0c;可以看到&#xff0c;消费电子行业正在逐渐回暖。国金证券在今年1月的研报中就指出&#xff0c;从多方面…