Apache Thrift、Google Protocol Buffers、MessagePack、ASN.1和Apache Avro之间的关键区别是什么?

134
所有这些工具都提供二进制序列化、RPC框架和IDL。我想了解它们之间的主要区别和特点(性能、易用性、编程语言支持等)。
如果您知道其他类似的技术,请在回答中提及。

7
好的,我会尽力为您翻译。以下是需要翻译的内容:http://en.wikipedia.org/wiki/Comparison_of_data_serialization_formats - Matthieu N.
@Zenikoder:该链接没有提供5个查询格式中的2个的任何信息。 - JUST MY correct OPINION
这个能帮到你吗:http://www.slideshare.net/IgorAnishchenko/pb-vs-thrift-vs-avro? - thoroc
2
对于那些不了解RPC(远程过程调用)和IDL(接口定义语言)的人来说。 - garg10may
6个回答

103

ASN.1是ISO/ISE标准,具有易读的源语言和各种二进制和人类可读的后端。作为国际标准(而且是一个古老的标准!),它的源语言有点杂乱(就像大西洋有点潮湿一样),但是它被极好地规范化了,并且得到了相当多的支持。(如果你费劲挖掘,可能会在任何命名的语言中找到ASN.1库,如果没有,也可以使用FFI中可用的良好的C语言库。)作为标准化语言,它有着过度详细的文档并且有一些不错的教程可用。

Thrift不是一个标准。它最初来自Facebook,后来成为顶级Apache项目并开源。它的文档不太充足,尤其是教程层面,而且根据我的(虽然简短)观察,似乎没有添加其他早期努力已经做到的东西(在某些情况下还做得更好)。公平地说,它支持的语言种类相当惊人,包括一些较高知名度的非主流语言。IDL也比较类似于C语言。

Protocol Buffers不是一个标准。它是Google产品,正在向更广泛的社区发布。它在支持的语言方面有些局限(仅支持C++、Python和Java),但是对于其他语言有很多第三方支持(质量高低参差不齐)。Google几乎所有的工作都使用Protocol Buffers,因此它是经过实战测试、打磨精练的协议(虽然没有ASN.1打磨得那么精练)。它的文档比Thrift好得多,但是作为Google的产品,很可能是不稳定的(指的是一直在变,而不是不可靠)。IDL也类似于C语言。

以上所有系统都使用某种IDL定义的模式来生成目标语言的代码,然后用于编码和解码。Avro不是这样的。Avro的类型是动态的,其模式数据直接在运行时用于编码和解码(这在处理上显然有一些成本,但对于动态语言以及不需要标记类型等方面也有一些明显的优点)。它的模式使用JSON,如果已经有一个JSON库,支持在新语言中使用Avro会更容易一些。同样,像大多数重新发明轮子的协议描述系统一样,Avro也没有标准化。

个人而言,尽管我对它有爱恨情结,但我可能会在大多数RPC和消息传输目的中使用ASN.1,尽管它实际上没有RPC堆栈(您需要创建一个,但IOCs使得这变得足够简单)。


3
感谢详细的解释。但是关于版本控制,我听说protobuf可以处理,其他库呢?它们在通用情况下如何使用?此外,似乎Avro现在除了JSON之外还有具有类C语法的IDL。 - andreypopp
2
ASN.1通过...扩展标记或模块头中的EXTENSIBILITY IMPLIED自动支持手动版本控制。据我所知,Protocol Buffers支持手动版本控制。我不知道它是否支持类似隐含可扩展性的功能(而且我懒得查)。Thrift也支持一些版本控制,但同样需要手动处理,没有隐含的可扩展性。 - JUST MY correct OPINION
8
记录一下,Protocol Buffers 总是通过数字显式编码字段,在库级别上如果存在额外的字段不会出错,如果标记为可选或显式,则缺少的字段也不会出错。因此所有 Protocol Buffers 消息都具有“隐含可扩展性”。 - Kevin Cathcart
你说的IOCs是指控制反转吗?在PHP中,用什么来实现RPC堆栈呢?像XML-RPC扩展一样的东西?还是需要自己编写代码实现? - Stann
我所指的IOCs是ASN.1中的IOC结构,即信息对象类。 - JUST MY correct OPINION
4
Avro更加灵活,因为它允许动态地处理已定义的模式(schema),或者生成样板类(boilerplate classes)。根据我的经验,Avro非常强大:其优势在于其丰富的功能集,包括RPC生成器(这是与Thrift共同的功能)。 - Paolo Maresca

42

我们刚刚对序列化器进行了内部研究,以下是一些结果(也是我未来参考的资料!)

Thrift = 序列化 + RPC堆栈

最大的区别在于Thrift不仅是一个序列化协议,它还是一个完整的RPC堆栈,就像现代SOAP堆栈一样。因此,在序列化之后,对象可以(但不是必须)通过TCP/IP在机器之间发送。在SOAP中,您从完全描述可用服务(远程方法)和预期参数/对象的WSDL文档开始。这些对象通过XML发送。在Thrift中,.thrift文件完全描述了可用方法、预期参数对象,并且对象通过可用序列化器之一进行序列化(使用紧凑协议,一种高效的二进制协议,在生产中最受欢迎)。

ASN.1 = 祖先级别的协议

ASN.1是在80年代由电信人员设计的,与最近从计算机科学领域出现的序列化器相比,由于受限于有限的库支持,使用起来很麻烦。它有两个变体,DER(二进制)编码和PEM(ASCII)编码。两者都很快,但DER更快且更高效。事实上,ASN.1 DER可以轻松地跟上(有时甚至超越)它自身30年后设计的序列化器,这证明了它的设计非常精良。它非常紧凑,比Protocol Buffers和Thrift小,只被Avro打败。问题在于需要优秀的库来支持,目前Bouncy Castle似乎是C#/Java最好的库。ASN.1在安全和加密系统中占据主导地位,并不会消失,所以不要担心“未来的可靠性”。只需获取一个好的库即可......
MessagePack = 中等水平
它并不差,但既不是最快的,也不是最小的,也不是最好支持的。没有生产理由选择它。
共同点
除此之外,它们非常相似。大多数都是基本的TLV:Type-Length-Value原则的变体。
Protocol Buffers(由Google发起),Avro(基于Apache,用于Hadoop),Thrift(由Facebook发起,现在是Apache项目)和ASN.1(电信发起)都涉及到某种程度的代码生成,其中您首先使用序列化器特定格式表达数据,然后序列化器“编译器”将通过code-gen阶段为您的语言生成源代码。然后,您的应用程序源使用这些code-gen类进行IO。请注意,某些实现(例如:Microsoft的Avro库或Marc Gavel的ProtoBuf.NET)允许您直接装饰您的应用程序级POCO / POJO对象,然后该库直接使用那些装饰类而不是任何code-gen的类。我们已经看到这提供了性能提升,因为它消除了一个对象复制阶段(从应用程序级POCO / POJO字段到code-gen字段)。

一些结果和一个可以玩的实时项目

这个项目(https://github.com/sidshetye/SerializersCompare)比较了C#世界中重要的序列化器。 Java人员已经有了something similar

1000 iterations per serializer, average times listed
Sorting result by size
Name                Bytes  Time (ms)
------------------------------------
Avro (cheating)       133     0.0142
Avro                  133     0.0568
Avro MSFT             141     0.0051
Thrift (cheating)     148     0.0069
Thrift                148     0.1470
ProtoBuf              155     0.0077
MessagePack           230     0.0296
ServiceStackJSV       258     0.0159
Json.NET BSON         286     0.0381
ServiceStackJson      290     0.0164
Json.NET              290     0.0333
XmlSerializer         571     0.1025
Binary Formatter      748     0.0344

Options: (T)est, (R)esults, s(O)rt order, (S)erializer output, (D)eserializer output (in JSON form), (E)xit

Serialized via ASN.1 DER encoding to 148 bytes in 0.0674ms (hacked experiment!)

4
ASN.1也有BER(基本编码规则)、PER(紧凑编码规则)和XER(XML编码规则)。 DER是BER的一个变体,主要用于加密,因为它保证每个数据都有唯一的编码。 BER和PER可以比DER更有效率。大多数库处理DER。有些库无法正确处理所有BER结构。想了解更多信息的人可以访问:http://luca.ntop.org/Teaching/Appunti/asn1.html - Joe Steele
它还具有JER - JavaScript对象表示法编码规则。您还可以使用ECN(编码控制符号)定义自己的编码规则。这里有一个包含下载链接的规范良好的列表:https://www.oss.com/asn1/resources/standards-define-asn1.html - Dmitry
2
有两种变体,DER(二进制)编码和PEM(ASCII)编码。请记住PEM只是在BEGIN END注释中包含Base64编码的二进制数据。这些二进制数据可能是使用DER编码生成的,因此比较PEM和DER可能会很奇怪。 - RafalS

17

除了性能考虑,Uber最近在他们的工程博客上评估了几个库:

https://eng.uber.com/trip-data-squeeze/

对于他们来说,胜者是使用MessagePack + zlib进行压缩。

我们的目标是找到编码协议和压缩算法的组合,以最高速度获得最紧凑的结果。我们在2,219条来自Uber纽约市的伪随机匿名行程上测试了编码协议和压缩算法的组合(将数据置于JSON文本文件中)。

这里教训是你的需求决定了哪个库适合你。对于Uber而言,由于消息传递的无模式特性,他们不能使用基于IDL的协议,这排除了很多选项。此外,对于他们来说,不仅原始编码/解码时间起作用,还有数据存储的大小。

大小结果

Size Results

速度结果

enter image description here


15
ASN.1最大的特点就是它是为了“规范”而设计的,而不是用于实现。因此,在任何“真实”的编程语言中,它非常擅长隐藏/忽略实现细节。
ASN.1编译器的工作是将编码规则应用于asn1文件,并从二者生成可执行代码。编码规则可以使用编码符号(ECN)提供,也可以是标准化的规则,如BER/DER、PER、XER/EXER。
也就是说,ASN.1定义了类型和结构,编码规则定义了在网络传输中的编码方式,而编译器将其转换为您选择的编程语言。
据我所知,免费的编译器支持C、C++、C#、Java和Erlang。而商业编译器(价格昂贵且需要专利/许可证)非常灵活,通常完全更新,并且有时还支持更多的编程语言,请参考它们的网站(OSS Nokalva、Marben等)。
这种技术使得在完全不同的编程文化之间(例如,“嵌入式”人员和“服务器农民”)指定接口变得出奇地容易,只需使用asn.1文件、编码规则(如BER)和UML交互图。不用担心它是如何实现的,让每个人都使用“自己的东西”!对我来说,这种方法非常有效。
顺便说一下:在OSS Nokalva的网站上,你可以找到至少两本关于ASN.1的免费下载书籍(一本是Larmouth写的,另一本是Dubuisson写的)。
在我看来,大多数其他产品只是试图成为又一个RPC存根生成器,把大量精力放在序列化问题上。嗯,如果有人需要那个,也许可以满足他们的需求。但对我来说,它们看起来像是对Sun-RPC(来自80年代末)的重新发明,但是,嘿,那也很好用。

8
Microsoft的Bond(https://github.com/Microsoft/bond)在性能、功能和文档方面非常印象深刻。然而,截至2015年2月13日,它并不支持许多目标平台。我只能假设这是因为它非常新。当前它支持Python、C#和C++。它正在被微软广泛使用。我尝试过它,对于我作为一个C#开发者来说,使用bond比使用protobuf更好,但是我也用过thrift,唯一的问题是文档,我不得不尝试很多东西才能理解如何操作。
有关Bond的一些资源如下(https://news.ycombinator.com/item?id=8866694https://news.ycombinator.com/item?id=8866848https://microsoft.github.io/bond/why_bond.html

6
为了提高性能,可以参考jvm-serializers基准测试,它是针对Java平台的特定、小型消息测试,但也许可以帮助你。我认为,通常来说,性能不一定是最重要的区别。另外:永远不要把作者的话当作圣经;很多宣传声明都是虚假的(例如msgpack网站有一些可疑的声明;它可能很快,但信息非常简略,用例不太现实)。
一个重要的区别是是否必须使用模式(至少PB、Thrift;Avro可能是可选的;ASN.1我认为也是;MsgPack则不一定)。
此外,我认为能够使用分层、模块化设计是很好的;也就是说,RPC层不应该规定数据格式、序列化方式。不幸的是,大多数候选者都会紧密地捆绑这些。
最后,选择数据格式时,现在的性能并不排除使用文本格式。有一些飞快的JSON解析器(以及相当快的流式xml解析器);而考虑到脚本语言的互操作性和易用性,二进制格式和协议可能不是最好的选择。

谢谢分享经验,但我认为我仍然需要二进制格式(我有大量的数据),可能会坚持使用Avro。 - andreypopp
是的,这可能有道理。无论使用哪种格式,您都可能希望使用压缩(LZF很好,因为它非常快速地压缩/解压缩,与gzip/deflate相比)。 - StaxMan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接