常见序列化协议有哪些
序列化(serialization)是将对象序列化为二进制形式(字节数组),一般也将序列化称为编码(Encode),主要用于网络传输、数据持久化等。常见的序列化协议包括以下几种:
-
JSON(JavaScript Object Notation):
- 一种轻量级的数据交换格式,易于阅读和编写。
- 它基于JavaScript语言的一个子集,但是可以被多种编程语言使用。
- 支持复杂的数据结构,包括数组、对象、字符串、数字等。
- 前后兼容性高,数据格式简单,易于读写,序列化后数据较小,可扩展性好,兼容性好。
- 相对于XML,其协议比较简单,解析速度较快。但数据的描述性比XML差,不适合性能要求为毫秒级别的情况,且额外空间开销比较大。
- 适用于跨防火墙访问、可调式性要求高的情况、基于Web browser的Ajax请求,以及传输数据量相对小、实时性要求相对低(例如秒级别)的服务等场景。
-
XML(eXtensible Markup Language):
- 一种标记语言,用于描述数据的结构和内容。
- 具有良好的可扩展性和跨平台性,可以通过DTD(Document Type Definition)或者XSD(XML Schema Definition)定义数据的结构。
- 序列化数据只包含数据本身以及类的结构,不包括类型标识和程序集信息。类必须有一个将由XmlSerializer序列化的默认构造函数,且只能序列化公共属性和字段,不能序列化方法。
- 文件庞大,文件格式复杂,传输占带宽。
- 常用于做配置文件存储数据、实时数据转换等场景。
-
Protocol Buffers(protobuf):
- 由Google开发的一种二进制序列化协议。
- 使用简洁的接口描述语言来定义数据结构,并生成相应的代码进行序列化和反序列化操作。
- 具有高效的编码和解码速度,以及较小的数据体积。
- 序列化后码流小,性能高,是结构化数据存储格式。通过标识字段的顺序,可以实现协议的前向兼容,结构化的文档更容易管理和维护。
- 需要依赖于工具生成代码,且支持的语言相对较少,官方只支持Java、C++、Python。
- 适用于对性能要求高的RPC调用、具有良好的跨防火墙的访问属性的场景,也适合应用层对象的持久化。
-
Thrift:
- 由Facebook开发的一种跨语言的服务框架,也包含了一种二进制序列化协议。
- 使用IDL(Interface Description Language)来定义数据结构和服务接口,并生成相应的代码进行序列化和反序列化操作。
- 支持多种编程语言,并提供了高效的网络通信能力。
- 序列化后的体积小、速度快,支持多种语言和丰富的数据类型,对于数据字段的增删具有较强的兼容性,还支持二进制压缩编码。
- 但使用者较少,跨防火墙访问时不安全,不具有可读性,调试代码时相对困难。不能与其他传输层协议共同使用(例如HTTP),也无法支持向持久层直接读写数据,即不适合做数据持久化序列化协议。
- 常用于分布式系统的RPC解决方案等场景。
-
MessagePack:
- 一种高效的二进制序列化协议,类似于JSON。
- 将数据压缩为二进制格式,具有较小的数据体积和高速的编码解码能力。
- 支持多种编程语言,并且可以与JSON相互转换。
-
Avro:
- 一种基于Schema的二进制序列化协议,由Apache开发。
- 使用JSON来定义数据结构,并将数据编码为紧凑的二进制格式。
- 支持动态类型、架构演化和跨语言等特性。
- Binary格式在空间开销和解析性能方面可以和Protobuf媲美,Avro的产生解决了JSON的冗长和没有IDL的问题。
- 支持丰富的数据类型,简单的动态语言结合功能,具有自我描述属性,提高了数据解析速度。其快速可压缩的二进制数据形式可以实现远程过程调用RPC,并支持跨编程语言实现。
- 对于习惯于静态类型语言的用户可能不太直观。
- 适用于Hadoop中做Hive、Pig和MapReduce的持久化数据格式等场景。Avro的设计理念偏向于动态类型语言,因此对于动态语言为主的应用场景,Avro是更好的选择。
-
Fastjson:
- 一个Java语言编写的高性能功能完善的JSON库。
- 采用一种“假定有序快速匹配”的算法,把JSON Parse的性能提升到极致。
- 接口简单易用,是目前Java语言中最快的json库。
- 但过于注重快,而偏离了“标准”及功能性,代码质量不高,文档不全。
- 适用于协议交互、Web输出、Android客户端等场景。
为什么不推荐使用 JDK 自带的序列化
不推荐使用JDK自带的序列化(即java.io.Serializable)的原因主要有以下几点:
- 不支持跨语言调用:
- JDK自带的序列化方式高度依赖Java语言,序列化后的字节数组只能由Java语言反序列化,无法与其他编程语言(如C++、Python、Go等)进行无缝传输与反序列化。这在多语言、多平台交互的系统中会造成不便。
- 性能较差:
- JDK自带的序列化机制在序列化和反序列化过程中会产生较大的字节数组,增加了存储和传输的负担。
- 相较于其他高效的序列化框架(如Protobuf、Thrift等),JDK自带的序列化方式在性能上显得较低,序列化速度相对较慢。
- 存在安全隐患:
- JDK自带的序列化和反序列化机制容易受到反序列化攻击。攻击者可以通过构造恶意输入数据,利用反序列化过程中的漏洞执行任意代码,造成系统安全漏洞。
- 为了避免这些安全问题,需要开发者在序列化和反序列化过程中进行严格的校验和防护,这增加了开发的复杂性和风险。
- 可读性差:
- JDK自带的序列化方式生成的字节流是二进制数据,不易阅读和理解。这对于调试和排查问题来说是一个不利因素。
- 版本兼容性问题:
- 当类的定义发生变化时,如果没有妥善处理序列化ID(serialVersionUID)的一致性,可能会导致反序列化失败。这增加了维护和管理序列化数据的复杂性。