序列化框架对比
序列化的定义
序列化: 把对象转化为可传输的字节序列过程称为序列化。
反序列化: 把字节序列还原为对象的过程称为反序列化。
为什么要序列化
其实序列化最终的目的是为了对象可以 跨平台存储, 和进行网络传输。 而我们进行跨平台存储和网络传输的方式就是 IO, 而我们的 IO 支持的数据格式就是字节数组。
因为我们单方面的只把对象转成字节数组还不行, 因为没有规则的字节数组我们是没办法把对象的本来面目还原回来的, 所以我们必须在把对象转成字节数组的时候就制定一种规则 ** (序列化) ** , 那么我们从 IO 流里面读出数据的时候再以这种规则把对象还原回来 ** (反序列化) **。
如果我们要把一栋房子从一个地方运输到另一个地方去, 序列化就是我把房子拆成一个个的砖块放到车子里, 然后留下一张房子原来结构的图纸, 反序列化就是我们把房子运输到了目的地以后, 根据图纸把一块块砖头还原成房子原来面目的过程
序列化技术选型
序列化协议各有千秋, 不能简单的说一种序列化协议是最好的, 只能从你的当时环境下去选择最适合你们的序列化协议, 如果你要为你的公司项目进行序列化技术的选型, 那么主要从以下几个因素。
- 协议是否支持跨平台
如果你们公司有好多种语言进行混合开发, 那么就肯定不适合用有语言局限性的序列化协议, 要不然你 JDK 序列化出来的格式, 其他语言并没法支持。 - 序列化的速度
如果序列化的频率非常高, 那么选择序列化速度快的协议会为你的系统性能提升不少。 - 序列化出来的大小
如果频繁的在网络中传输的数据那就需要数据越小越好, 小的数据传输快, 也不占带宽, 也能整体提升系统的性能。 - 类库是否小巧, API 使用是否方便
- 使用者开发的工作量和难度
序列化的方式
序列化只是一种拆装组装对象的规则, 那么这种规则肯定也可能有多种多样。
现在常见的序列化方式有:
JDK (不支持跨语言) , JSON (Fastjson, Jackson, Gson), XML, Hessian, FST (不支持跨语言) , Kryo (不支持跨语言) , Thrift, Protobuf, Protostuff
JDK (不支持跨语言)
JDK 提供的序列化有以下几个缺点:
- 无法跨语言
- 序列后的码流大 (是二进制编码的 5 倍)
- 序列化性能低
- 易用性和可扩展性差
XML
XML 优缺点
优点
- 良好的可读性
- 序列化的数据包含完整的结构
- 调整不同属性的顺序对序列化/反序列化不影响
缺点
- 数据传输量大
- 不支持二进制数据类型
JSON (Fastjson, Jackson, Gson)
JSON 优缺点
优点
- 良好的可读性
- 调整不同属性的顺序对序列化/反序列化不影响
缺点
- 丢弃了类型信息, 比如”price”:100, 对 price 类型是 int/double 解析有二义性
- 不支持二进制数据类型
Hessian
Hessian 是一个基于 binary-RPC 实现的远程通讯 library。使用二进制传输数据。Hessian 通常通过 Web 应用来提供服务, 通过接口暴露。Servlet 和 Spring 的 DispatcherServlet 都可以把请求转发给 Hessian 服务。由以下两种方式提供, 分别为: com.caucho.hessian.server.HessianServlet, org.springframework.web.servlet.DispatcherServlet。
Hessian 是一个轻量级的 remoting on http 工具, 使用简单的方法提供了 RMI (Java Remote Method Invocation 方法远程调用) 的功能。 相比 WebService, Hessian 更简单, 快捷。采用的是二进制 RPC ( (RemoteProcedureCallProtocol) 远程过程调用协议) 协议, 因为采用的是二进制协议, 所以它很适合于发送二进制数据。
Hessian 优缺点
Hessian 优点
- 简单易用, 面向接口, 通过接口暴露服务, jar 包只有 200, 300k, 不需要配置防火墙
- 效率高, 复杂对象序列化速度仅次于 RMI, 简单对象序列化优于 RMI, 二进制传输
- 多语言支持: wiki, Java, Flash/Flex, Python, C++, .NET C#, PHP, Ruby, Objective-C
- 可与 spring 集成, 配置简单, HessianServiceExporter
Hessian 缺点
- 缺乏安全机制, 传输没有加密处理
- 异常机制不完善, 总是报一些错误, 错误原因也是千奇百怪, 提示信息不足
- 事务处理欠缺
- 版本问题, spring 2.5.6 对照 3.1.3 版, spring 3 对照 4.0 及以上版本, 需要使用 spring MVC
FST (全称: Fast Serialization, 不支持跨语言)
FST 序列化全称是 Fast Serialization Tool, 它是对 Java 序列化的替换实现, 在 dubbo 中有使用。
官方文档: fast-serialization-wiki#2x-documentation
FST 优缺点
FST 优点
- 比 JDK 提供的序列化提升了 10 倍, 体积也减少至 1/3 —— 1/4
- 支持堆外 Maps, 和堆外 Maps 的持久化
- 支持序列化为 JSON
FST 缺点
- 文档较少, 社区不活跃, 使用案例少
- 长期未更新, 最后一次更新是 2018 年
Kryo (不支持跨语言)
Kryo 是一个快速序列化/反序列化工具, 依赖于字节码生成机制 (底层使用了 ASM 库), 因此在序列化速度上有一定的优势, 但正因如此, 其使用也只能限制在基于 JVM 的语言上。
和 Hessian 类似, Kryo 序列化出的结果, 是其自定义的、独有的一种格式。由于其序列化出的结果是二进制的, 也即 byte[], 因此像 Redis 这样可以存储二进制数据的存储引擎是可以直接将 Kryo 序列化出来的数据存进去。当然你也可以选择转换成 String 的形式存储在其他存储引擎中 (性能有损耗) 。
Kryo 优缺点
Kryo 优点
- 速度快, 序列化后体积小
Kryo 缺点
- 跨语言支持较复杂
- 生成的 byte 数据中部包含 field 数据, 对类升级的兼容性很差
- 每一个类都要注册
- 类要有一个空的构造函数, 不然不能序列化
Thrift
协议
Thrift 可以让用户选择客户端与服务端之间传输通信协议的类别, 在传输协议上总体划分为文本(text)和二进制(binary)传输协议。为节约带宽, 提高传输效率, 一般情况下使用二进制类型的传输协议为多数, 有时还会使用基于文本类型的协议, 这需要根据项目/产品中的实际需求。常用协议有以下几种:
- TBinaryProtocol: 二进制编码格式进行数据传输
- TCompactProtocol: 高效率的, 密集的二进制编码格式进行数据传输
- TJSONProtocol: 使用 JSON 文本的数据编码协议进行数据传输
- TSimpleJSONProtocol: 只提供 JSON 只写的协议, 适用于通过脚本语言解析
- TDebugProtocol: 在开发的过程中帮助开发人员调试用的, 以文本的形式展现方便阅读
传输层
一个 server 只允许定义一个接口服务。这样的话多个接口需要多个 server。这样会带来资源的浪费。通常可以通过定义一个组合服务来解决。
- TSocket: 使用堵塞式 I/O 进行传输, 也是最常见的模式。
- TFramedTransport: 使用非阻塞方式, 按块的大小, 进行传输, 类似于 Java 中的 NIO。
- TFileTransport: 顾名思义按照文件的方式进程传输, 虽然这种方式不提供 Java 的实现, 但是实现起来非常简单。
- TMemoryTransport: 使用内存 I/O, 就好比 Java 中的 ByteArrayOutputStream 实现。
- TZlibTransport: 使用执行 zlib 压缩, 不提供 Java 的实现。
Thrift 优缺点
优点
- 支持非常多的语言绑定
- thrift 文件生成目标代码, 简单易用
- 消息定义文件支持注释
- 数据结构与传输表现的分离, 支持多种消息格式
- 包含完整的客户端/服务端堆栈, 可快速实现 RPC
- rpc 支持 thread pool, hsha, no blocking 多种形式
- RPC 和序列化性能都不错, 这个到处都有 benchmark, 并不是性能最好的, 但是基本上不会成为瓶颈或者短板
- One-stop shop, 相对于 protobuf, 序列化和 RPC 支持一站式解决, 如果是 pb 的话, 还需要考虑选择 RPC 框架, 现在 Google 是开源了 gRpc, 但是几年以前是没有第一方的标准解决方案的
- 支持同步和异步通信
- 有很多开源项目的周边支持都是 thrift 的 (hbase 提供 thrift 服务, hive, spark sql, cassandra 等一系列对外的标准服务接口都是 thrift 的以支持多语言)
缺点
- 和 protobuf 一样不支持动态特性
- 基本没有官方文档 (使用参考可以看看有人专门写的这个 Thrift: The Missing Guide)
- bug fix 和更新不积极 (好在序列化和 RPC 服务都不是太复杂的问题, 需要考量的设计问题不多, 自己维护 patch 的成本不高)
- RPC 在 0.6.1 升级到 0.7.0 是不兼容的!
- 0.6.1 的 java 的 ThreadPool Server 是会有 Thread 死亡之后的 Thread 泄露问题
Protobuf
protobuf 优缺点
优点
- 性能好, 效率高
- 时间和空间开销更优
- 有代码生成机制, 简单易用
- 编写 proto 文件, 可以通过编译器生成对应语言的类
- 支持向后兼容和向前兼容
- 当客户端和服务器同时使用一个协议时, 服务器在协议中增加一个字节, 并不会影响客户端的使用
- 支持多种编程语言 (可以把 proto 文件看做 IDL 文件)
- 在 Google 官方发布的源代码中包含了
- C++
- C#
- Dart
- Go
- Java
- Kotlin
- Python
- Netty 等一些框架集成
- 二进制格式让信息读取有门槛
缺点
- 二进制格式导致可读性差
- 缺乏自描述
- 默认不具备动态特性
- 通用性相比 json 和 XML 差一点
Protostuff
protostuff 是 protobuf 的改良版本, 可以直接将一个 java object 进行序列化, 使用方法与 kyro 有点类似, 没有 protobuf 的 proto 文件编写过程。
Protostuff 优缺点
Protostuff 优点
- 基于 protobuf, 有 protobuf 的优点
Protostuff 缺点
- 有 protobuf 的缺点
- 省去 proto 文件编写过程的同时, 也失去了 protobuf IDL 的特性, 跨平台开发变得复杂
zfoo protocol (新序列化框架)
2021 年出现的新序列化框架
zfoo protocol 优缺点
zfoo protocol 优点
- 速度快, 性能好, 性能测试文章: Java常见的序列化和反序列化框架的性能, 速度, 大小pk
- 文档和教程完善
zfoo protocol 缺点
- 没有经过大型商业或开源项目验证