Raj*_*j V 1 serialization thrift protocol-buffers avro parquet
我正在阅读有关使用 java 序列化的缺点以及使用序列化框架的必要性的文章。有很多框架,例如 avro、parquet、thrift、protobuff。
问题是哪个框架解决了什么问题以及选择序列化框架时需要考虑的所有参数是什么。
我想尝试一个实际用例,并根据需求比较/选择序列化框架。
有人可以就这个话题提供帮助吗?
有很多因素需要考虑。我将介绍一些重要的内容。
0) 模式优先或代码优先
如果您有一个涉及不同语言的项目,那么代码优先方法可能会出现问题。拥有一个可以序列化的 JAVA 类固然很好,但如果必须在 C 中反序列化它可能会很麻烦。
一般来说,我更喜欢模式优先的方法,以防万一。
1) 对象间划分
某些序列化会生成字节流,使您可以看到一个对象在哪里停止而另一个对象在哪里开始。其他没有。
因此,如果您有一个消息传输/数据存储可以为您分离出一批字节,例如 ZeroMQ 或数据库字段,那么您可以使用不区分消息的序列化。示例包括 Google Protocol Buffers。通过传输/存储完成的划分,读取器可以获得一批字节,并确定它包含一个对象,并且仅包含一个对象。
如果您的消息传输/数据存储没有在字节批次(例如网络流或文件)之间进行划分,那么您要么发明自己的划分标记,要么使用为您划分的序列化。示例包括 ASN.1 BER、XML。
2) 规范
这是序列化的一个属性,这意味着序列化的数据描述其自己的结构。原则上,规范消息的读者不必预先知道消息结构是什么,它可以在读取字节时简单地计算出来(即使它不知道字段名称)。这在您不完全确定数据来自何处的情况下非常有用。如果数据不规范,读者必须提前知道对象结构是什么,否则反序列化是不明确的。
规范序列化的示例包括 ASN.1 BER、ASN.1 规范 PER、XML。不包括 ASN.1 uPER 的,可能是 Google Protocol Buffers(我可能有这个错误)。
AVRO 做了一些不同的事情 - 数据模式本身就是序列化数据的一部分,因此总是可以从任意数据重建对象。正如您可以想象的那样,用于此目的的库在 C 等语言中有些笨重,但在动态语言中则更好。
3) 规模和价值受限。
某些序列化技术允许开发人员对字段值和数组大小设置约束。目的是从包含此类约束的模式文件生成的代码将自动验证序列化和反序列化时的对象。
这非常有用 - 自动完成免费、模式驱动的内容检查。很容易发现不符合规格的数据。
这在大型异构项目(使用许多不同的语言)中非常有用,因为关于什么是有效的、什么是无效的所有事实来源都来自模式,并且仅来自模式,并且由自动生成的代码自动强制执行。开发人员无法忽略/绕过约束,当约束发生变化时,每个人都会情不自禁地注意到。
示例包括 ASN.1(通常通过工具集可以很好地完成)、XML(免费/廉价的工具集通常不能正确完成;MS 的 xsd.exe 故意忽略任何此类约束)和 JSON(具体到对象验证器)。在这三者中,ASN.1 具有迄今为止最复杂的约束语法;它真的非常强大。
不支持的示例 - Google Protocol Buffers。在这方面GPB是极其令人恼火的,因为它根本没有约束。具有值和大小限制的唯一方法是将它们作为注释写入 .proto 文件中并希望开发人员阅读它们并注意,或者使用其他某种非源代码方法。由于 GPB 主要针对异构系统(实际上支持世界上的每种语言),我认为这是一个非常严重的遗漏,因为必须为项目中使用的每种语言手动编写值/大小验证代码。那是浪费时间。Google 可以向 .proto 和代码生成器添加语法元素来支持这一点,而无需更改线路格式(全部在自动生成的代码中)。
4) 二进制/文本
二进制序列化会更小,并且序列化/反序列化可能会更快一些。文本序列化更容易调试。但使用二进制序列化可以完成的事情令人惊奇。例如,可以轻松地将 ASN.1 解码器添加到 Wireshark(您使用 ASN.1 工具从 .asn 模式文件编译它们),等等 - 程序数据的在线解码。我认为 GPB 也可以实现同样的效果。
ASN.1 uPER 在带宽受限的情况下非常有用;它会自动使用大小/值约束来节省线路上的位数。例如,在 0 到 15 之间有效的字段只需要 4 位,而这正是 uPER 将使用的。我认为 uPER 在 3G、4G 和 5G 等协议中也发挥着重要作用,这并非巧合。这种“最小位”方法比压缩文本线格式要优雅得多(这是对 JSON 和 XML 所做的很多工作,以减少它们的臃肿)。
5)价值观
这有点奇怪。在 ASN.1 中,模式文件既可以定义对象的结构,也可以定义对象的值。有了更好的工具,您最终会得到(在 C++、JAVA 等源代码中)类,并且预定义该类的对象已经填充了值。
为什么这有用?嗯,我经常使用它来定义项目常量,并提供对约束的限制。例如,假设消息中有一个有效长度为 15 的数组字段。您可以在字段约束中使用文字 15,也可以在约束中引用整数值对象的值,并且该整数也可供开发人员使用。
--ASN.1 value that, in good tools, is built into the
--generated source code
arraySize INTEGER ::= 16
--A SET that has an array of integers that size
MyMessage ::= SET
{
field [0] SEQUENCE (SIZE(arraySize)) OF INTEGER
}
Run Code Online (Sandbox Code Playgroud)
在您想要循环该约束的情况下,这非常方便,因为循环可以是
for (int i = 0; i < arraySize; i++) {do things with MyMessage.field[i];} // ArraySize is an integer in the auto generated code built from the .asn schema file
Run Code Online (Sandbox Code Playgroud)
显然,如果需要更改约束,这是非常棒的,因为唯一需要更改的地方是在架构中,然后是项目重新编译(其中使用的每个地方都将使用新值)。更好的是,如果它在模式文件中重命名,则重新编译会标识它所使用的项目中的所有位置(因为使用它的开发人员编写的源代码仍然使用旧名称,它现在是一个未定义的符号 --> 编译器错误。
ASN.1 约束可能会变得非常复杂。以下是我们可以做的一些小尝试。这对于系统开发人员来说非常棒,但对于工具开发人员来说实现起来却相当复杂。
arraySize INTEGER ::= 16
minSize INTEGER ::= 4
maxVal INTEGER ::= 31
minVal INTEGER ::= 16
oddVal INTEGER ::= 63
MyMessage2 ::= SET
{
field_1 [0] SEQUENCE (SIZE(arraySize)) OF INTEGER, -- 16 elements
field_2 [1] SEQUENCE (SIZE(0..arraySize)) OF INTEGER, -- 0 to 16 elements
field_3 [2] SEQUENCE (SIZE(minSize..arraySize)) OF INTEGER, -- 4 to 16 elements
field_4 [3] SEQUENCE (SIZE(minSize<..<arraySize)) OF INTEGER, -- 5 to 15 elements
field_5 [4] SEQUENCE (SIZE(minSize<..<arraySize)) OF INTEGER(0..maxVal), -- 5 to 15 elements valued 0..31
field_6 [5] SEQUENCE (SIZE(minSize<..<arraySize)) OF INTEGER(minVal..maxVal), -- 5 to 15 elements valued 16..31
field_7 [6] SEQUENCE (SIZE(minSize<..<arraySize)) OF INTEGER(minVal<..maxVal), -- 5 to 15 elements valued 17..31
field_8 [7] SEQUENCE (SIZE(arraySize)) OF INTEGER(minVal<..<maxVal), -- 16 elements valued 17..30
field_9 [8] INTEGER (minVal..maxVal AND oddVal) -- valued 16 to 31, and also 63
f8_indx [10] INTEGER (0..<arraySize) -- index into field 8, constrained to be within the bounds of field 8
}
Run Code Online (Sandbox Code Playgroud)
据我所知,只有 ASN.1 这样做。然后,只有更昂贵的工具才能真正从架构文件中提取这些元素。有了它,这使得它在大型项目中非常有用,因为实际上与数据及其约束以及如何处理它相关的所有内容都仅在 .asn 模式中定义,而没有在其他地方定义。
正如我所说,我经常使用它来完成正确类型的项目。一旦将其渗透到整个项目中,所节省的时间和风险将是惊人的。它也改变了项目的动态;人们可以对模式进行后期更改,因为知道整个项目只需重新编译即可接受这些更改。因此,项目后期的协议更改会从高风险变成您可能每天都乐意做的事情。
6) 线格式对象类型
一些序列化线格式将识别线格式字节中对象的类型。这有助于读者应对多种不同类型的对象可能来自一个或多个源的情况。其他系列不会。
ASN.1 因线格式而异(它有多种,包括一些二进制格式以及 XML 和 JSON)。ASN.1 BER 在其线格式中使用类型、值和长度字段,因此阅读器可以预先查看对象的标签,从而相应地解码字节流。这非常有用。
Google Protocol Buffers 并不完全做同样的事情,但是如果 .proto 中的所有消息类型都捆绑到一个 Final 中oneof,并且只有每个序列化,那么您就可以实现相同的目标
7)工具成本。
ASN.1 工具的范围从非常非常昂贵(而且非常好)到免费(而且不太好)。许多其他工具都是免费的,但我发现最好的 XML 工具(适当注意值/大小限制)也相当昂贵。
8) 语言覆盖范围
如果您听说过它,那么许多不同语言的工具都可能涵盖它。如果没有,那就少了。
好的商业 ASN.1 工具涵盖 C/C++/Java/C#。有一些免费的 C/C++ 版本,其完整性各不相同。
9) 质量
如果工具的质量很差,那么采用序列化技术是没有好处的。
根据我的经验,GPB 很好(它通常会按照它所说的那样做)。商业ASN1工具非常好,全面超越GPB的工具集。AVRO 有效。我听说 Capt'n Proto 偶尔会出现一些问题,但由于我自己没有使用过,所以您必须检查一下。XML 与良好的工具一起工作。
10)总结
如果您不知道的话,我是 ASN.1 的忠实粉丝。
GPB 因其广泛的支持和熟悉而非常有用,但我确实希望 Google 能为字段和数组添加值/大小限制,并包含值表示法。如果他们这样做,就有可能拥有与 ASN.1 相同的项目工作流程。如果 Google 仅添加这两个功能,我认为 GPB 已经接近“完整”,只需要相当于 ASN.1 的 uPER 即可为那些存储空间或带宽很少的人完成它。
请注意,其中很多内容都集中在项目的情况以及技术实际上有多好/快/成熟。
| 归档时间: |
|
| 查看次数: |
1346 次 |
| 最近记录: |