# Netty 编解码技术

# Java序列化的目的

  • 网络传输
  • 对象持久化

# Java序列化的缺点

# 无法跨语言

  • 无法跨语言,是Java序列化最致命的问题。对于跨进程的服务调用,服务提供者可能会使用C++或者其他语言开发,当我们需要和异构语言进程交互时,Java序列化就难以胜任。
  • 由于Java序列化技术是Java语言内部的私有协议,其他语言并不支持,对于用户来说它完全是黑盒。对于Java序列化后的字节数组,别的语言无法进行反序列化,这就严重阻碍了它的应用。
  • 事实上,目前几乎所有流行的Java RCP通信框架,都没有使用Java序列化作为编解码框架,原因就在于它无法跨语言,而这些RPC框架往往需要支持跨语言调用。

# 序列化后的码流太大

  • 据测试验证:采用JDK序列化机制编码后的二进制数组大小竟然是二进制编码的5.29倍。
  • 我们评判一个编解码框架的优劣时,往往会考虑以下几个因素。
    • 是否支持跨语言,支持的语言种类是否丰富;
    • 编码后的码流大小;
    • 编解码的性能;
    • 类库是否小巧,API使用是否方便;
    • 使用者需要手工开发的工作量和难度。
  • 在同等情况下,编码后的字节数组越大,存储的时候就越占空间,存储的硬件成本就越高,并且在网络传输时更占带宽,导致系统的吞吐量降低。Java序列化后的码流偏大也一直被业界所诟病,导致它的应用范围受到了很大限制。

# 序列化性能太低

  • 据测试验证:Java序列化的性能只有二进制编码的6.17%左右

# 业界主流的编解码框架

# Google的Protobuf介绍

Protobuf全称Google Protocol Buffers,它由谷歌开源而来,在谷歌内部久经考验。它将数据结构以.proto文件进行描述,通过代码生成工具可以生成对应数据结构的POJO对象和Protobuf相关的方法和属性。

它的特点如下。

  • 结构化数据存储格式(XML,JSON等);
  • 高效的编解码性能;
  • 语言无关、平台无关、扩展性好;
  • 官方支持Java、C++和Python三种语言。

首先我们来看下为什么不使用XML,尽管XML的可读性和可扩展性非常好,也非常适合描述数据结构,但是XML解析的时间开销和XML为了可读性而牺牲的空间开销都非常大,因此不适合做高性能的通信协议。Protobuf使用二进制编码,在空间和性能上具有更大的优势。

Protobuf另一个比较吸引人的地方就是它的数据描述文件和代码生成机制,利用数据描述文件对数据结构进行说明的优点如下。

  • 文本化的数据结构描述语言,可以实现语言和平台无关,特别适合异构系统间的集成;
  • 通过标识字段的顺序,可以实现协议的前向兼容;
  • 自动代码生成,不需要手工编写同样数据结构的C++和Java版本;
  • 方便后续的管理和维护。相比于代码,结构化的文档更容易管理和维护。

# Facebook的Thrift介绍

Thrift源于Facebook,在2007年Facebook将Thrift作为一个开源项目提交给了Apache基金会。对于当时的Facebook来说,创造Thrift是为了解决Facebook各系统间大数据量的传输通信以及系统之间语言环境不同需要跨平台的特性,因此Thrift可以支持多种程序语言,如C++、C#、Cocoa、Erlang、Haskell、Java、Ocami、Perl、PHP、Python、Ruby和Smalltalk。

在多种不同的语言之间通信,Thrift可以作为高性能的通信中间件使用,它支持数据(对象)序列化和多种类型的RPC服务。Thrift适用于静态的数据交换,需要先确定好它的数据结构,当数据结构发生变化时,必须重新编辑IDL文件,生成代码和编译,这一点跟其他IDL工具相比可以视为是Thrift的弱项。Thrift适用于搭建大型数据交换及存储的通用工具,对于大型系统中的内部数据传输,相对于JSON和XML在性能和传输大小上都有明显的优势。

Thrift主要由5部分组成。

  • 语言系统以及IDL编译器:负责由用户给定的IDL文件生成相应语言的接口代码;
  • TProtocol:RPC的协议层,可以选择多种不同的对象序列化方式,如JSON和Binary;
  • TTransport:RPC的传输层,同样可以选择不同的传输层实现,如socket、NIO、MemoryBuffer等;
  • TProcessor:作为协议层和用户提供的服务实现之间的纽带,负责调用服务实现的接口;
  • TServer:聚合TProtocol、TTransport和TProcessor等对象。

我们重点关注的是编解码框架,与之对应的就是TProtocol。由于Thrift的RPC服务调用和编解码框架绑定在一起,所以,通常我们使用Thrift的时候会采取RPC框架的方式。但是,它的TProtocol编解码框架还是可以以类库的方式独立使用的。

与Protobuf比较类似的是,Thrift通过IDL描述接口和数据结构定义,它支持8种Java基本类型、Map、Set和List,支持可选和必选定义,功能非常强大。因为可以定义数据结构中字段的顺序,所以它也可以支持协议的前向兼容。

Thrift支持三种比较典型的编解码方式。

  • 通用的二进制编解码;
  • 压缩二进制编解码;
  • 优化的可选字段压缩编解码。

# JBoss Marshalling介绍

JBoss Marshalling是一个Java对象的序列化API包,修正了JDK自带的序列化包的很多问题,但又保持跟java.io.Serializable接口的兼容。同时,增加了一些可调的参数和附加的特性,并且这些参数和特性可通过工厂类进行配置。

相比于传统的Java序列化机制,它的优点如下。

  • 可插拔的类解析器,提供更加便捷的类加载定制策略,通过一个接口即可实现定制;
  • 可插拔的对象替换技术,不需要通过继承的方式;
  • 可插拔的预定义类缓存表,可以减小序列化的字节数组长度,提升常用类型的对象序列化性能;
  • 无须实现java.io.Serializable接口,即可实现Java序列化;
  • 通过缓存技术提升对象的序列化性能。

相比于前面介绍的两种编解码框架,JBoss Marshalling更多是在JBoss内部使用,应用范围有限。

# MessagePack编解码

MessagePack是一个高效的二进制序列化框架,它像JSON一样支持不同语言间的数据交换,但是它的性能更快,序列化之后的码流也更小。 MessagePack在业界得到了非常广泛的应用。

MessagePack 的特点如下:

  • 编解码高效,性能高;
  • 序列化之后的码流小;
  • 支持跨语言。

多语言支持:

衡量序列化框架通用性的一个重要指标就是对多语言的支持,因为数据交换的双方很难保证一定采用相同的语言开发,如果序列化框架和某种语言绑定,它就很难跨语言,例如Java的序列化机制。 MessagePack提供了对多语言的支持,官方支持的语言如下:Java、Python、Ruby、Haskell、C#、OCaml、Lua、Go、C、C++等。更详细的支持列表可以参见它的官网:http://msgpack.org/。

Last Updated: 5/18/2021, 7:48:51 PM