你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
许多云应用程序使用异步消息在系统组件之间交换信息。 消息传送的一个重要方面是用于对有效负载数据进行编码的格式。 在选择消息传送技术后,下一步便是定义消息的编码方式。 虽有多个选项可供选择,但正确的选择取决于用例。
本文描述了部分注意事项。
消息交换需求
在生成者和使用者之间交换消息需要:
- 可以定义消息的有效负载的形状或结构。
- 用于表示有效负载的编码格式。
- 用于读取和写入编码负载的序列化库。
消息的生成者根据业务逻辑及其要发送给使用者的信息定义消息形状。 若要构造形状,可将信息划分为离散主题或相关主题(或 字段)。 确定这些字段的值的特征。 请考虑以下问题。
- 什么是最有效的数据类型?
- 有效负载是否始终包含特定字段?
- 有效负载是否具有单个记录或重复的值集?
然后根据需求选择编码格式。 具体因素包括创建高度结构化数据(如果需要)、编码和传输消息所需的时间,以及分析有效负载的能力。 然后选择满足需求的编码格式。
使用者必须了解这些决策才能正确读取传入消息。
若要传送消息,生成者需将消息序列化为某种编码格式。 在接收端,使用者需对有效负载进行反序列化,方可访问数据。 此过程可确保这两个实体共享同一模型。 只要形状保持不变,消息传送将继续没有任何问题。 但当协定发生变化时,编码格式应能够应对变化,而且不会中断使用者。
某些编码格式(如 JSON)是自我描述的,这意味着可以在不引用架构的情况下进行分析。 但是,这些格式通常会生成更大的消息。 其他格式可能无法轻松地分析数据,但它们会导致更紧凑的消息。 本文概述了帮助你选择正确格式的关键因素。
编码格式注意事项
编码格式定义如何以字节形式表示一组结构化数据。 消息类型可能会影响格式的选择。 与业务事务相关的消息最有可能包含高度结构化的数据。 此外,你可能希望稍后检索结构化数据以进行审核。 对于事件流,可能希望尽快读取记录序列,并将其存储起来以供统计分析。
选择编码格式时,请考虑以下因素。
用户可读性
消息编码可以大致分为基于文本的格式和二进制格式。
使用基于文本的编码,消息有效负载为纯文本,因此用户无需使用代码库即可对其进行检查。 此方法使数据更易于读取和理解。 人工可读格式适用于存档数据。 由于人类可以读取有效负载,因此基于文本的格式更易于调试并发送到日志,以便排查错误。
基于文本的编码的缺点是有效负载往往更大。 数据负载大小通常可以通过代码压缩过程来减少,前提是可以在需要时恢复以便于人工阅读。 基于文本的常见格式为 JSON 和 YAML。
加密
如果消息中有敏感数据,请考虑这些消息是否应 全部加密。 或者,如果只需要加密特定字段,并且希望降低云成本,请考虑使用 NServiceBus 等库。
编码大小
消息大小会影响网络上的网络输入/输出性能。 二进制格式较基于文本的格式更简洁。 二进制格式需要序列化和反序列化库。 有效负载只有被解码后才能被读取。
如需减少网络占用情况并更快地传送消息,请使用二进制格式。 在需要考虑存储或网络带宽的情况下,建议使用此类格式。 二进制格式选项包括 Apache Avro、Google 协议缓冲区 (protobuf)、MessagePack 和简明二进制对象展现 (CBOR)。 这些格式的优缺点稍后在 “编码格式选项”中介绍。
二进制格式的缺点是有效负载对人类来说不可读。 大多数二进制格式使用复杂的系统,此类系统的维护成本可能很高。 此外,他们需要专用库来解码,而如果想要检索存档数据,则这些专用库可能不受支持。
对于非二元格式,缩小过程会删除不必要的空格和字符,同时保留格式规范的符合性。 此方法有助于在不更改结构的情况下减小编码大小。 评估编码器的功能,使缩小为默认值。 例如,.NET 的 JsonSerializerOptions.WriteIndented
中的 System.Text.Json.JsonSerializer
可以在创建 JSON 文本时控制自动压缩。
了解有效负载
消息有效负载作为字节序列送达。 若要分析此序列,使用者必须能够访问用于描述有效负载中的数据字段的元数据。 存储和分发元数据的两种主要方法是:
标记的元数据。 在某些编码格式(尤其是 JSON)中,字段在消息正文中用数据类型和标识符进行标记。 这些格式为自述性,原因是可以在不引用架构的情况下将其分析为值字典。 使用者了解字段的一种方法是查询预期值。 例如,生成者会以 JSON 格式发送有效负载。 使用者将 JSON 分析为字典并检查是否存在字段,以便了解有效负载。 另一种方法是使用者应用生成者共享的数据模型。 例如,如果使用静态类型化语言,许多 JSON 序列化库可以将 JSON 字符串分析为类型化类。
架构。 架构可正式定义消息的结构和数据字段。 在此模型中,生产者和消费者通过一个定义完善的模式具有合同。 架构可以定义数据类型、必需字段或可选字段、版本信息和有效负载的结构。 生成者根据编写器架构发送有效负载。 而使用者可以通过应用读取器架构来接收有效负载。 消息是使用针对特定编码的库进行序列化和反序列化的。 可以通过两种方式分发架构:
将架构存储为消息中的前言或标头,但与有效负载分开。
在外部存储架构。
某些编码格式可以定义架构,并使用可以通过架构生成类的工具。 生成者和使用者均可使用这些类和库来序列化和反序列化有效负载。 这些库还可在编写器和读取器架构之间提供兼容性检查。 Protobuf 和 Apache Avro 均遵循这种方法。 主要区别在于 protobuf 具有与语言无关的架构定义,Avro 使用压缩 JSON。 另一个不同之处在于这两种格式在读取器和编写器架构之间提供兼容性检查的方式。
在外部存储架构的另一种方法是架构注册表。 该消息包含架构引用和有效负载。 生成者在消息中发送架构标识符。 使用者通过指定外部存储中的标识符来检索架构。 双方都使用特定于格式的库来读取和写入消息。 除了存储架构之外,注册表还可以提供兼容性检查,以确保生成者和使用者之间的协定不会随着架构的发展而中断。
在选择方法之前,确定传输数据大小还是以后分析存档数据的能力更重要。
将架构与有效负载一起存储会产生更大的编码大小,非常适合间歇性消息。 如果传输较小的字节块很重要,或者需要记录序列,请选择此方法。 维护外部架构存储的成本可能很高。
但是,如果按需解码有效负载比大小更重要,则包括含有效负载的架构或标记元数据方法可为以后解码提供保障。 消息大小可能会显著增加,这会影响存储成本。
架构版本控制
随着业务需求的变化,预计形状也会有所改变,并且架构也将不断演变。 版本控制允许生成者指示可能包含新功能的架构更新。 版本控制具有两个关键方面:
使用者应跟踪和了解更改。
一种方式是让使用者检查所有字段,确定架构是否已更改。 另一种方式是让生成者发布含消息的架构版本号。 生成者会随架构演变递增版本。
更改不得影响或破坏使用者的业务逻辑。
假设一个字段被添加到现有模式中。 如果使用新版本的使用者根据旧版本获取有效负载,则如果他们无法忽略缺少新字段,其逻辑可能会打破。 现在,请考虑相反的方案。 如果新架构中删除了字段,则使用旧架构的使用者可能无法读取数据。
Avro 等编码格式提供了定义默认值的功能。 在前面的示例中,如果使用默认值添加字段,则缺少的字段将填充默认值。 protobuf 等其他格式则可通过必填字段和可选字段提供类似功能。
有效负载结构
请考虑有效负载中的数据是结构化为记录序列还是单个离散有效负载。 有效负载结构可以分为以下模型之一:
数组/字典/值: 定义在一个或多个多维数组中保存值的条目。 条目具有唯一键/值对。 可以扩展模型来表示复杂结构。 一些示例包括 JSON、Apache Avro 和 MessagePack。
如果消息使用不同的架构单独编码,则此布局适用。 如果有多个记录,有效负载可能会过度冗余。 此冗余可能导致有效负载膨胀。
表格数据: 信息分为行和列。 每列指示一个字段或信息的主题,每一行都包含这些字段的值。 此布局对于一组重复的信息(如时序数据)十分有效。
Comma-Separated 值(CSV)是最简单的基于文本的格式之一。 该格式以具有通用标头的记录序列形式呈现数据。 对于二进制编码,Apache Avro 的报头类似于 CSV 标头,但生成的编码更精简。
库支持
应使用已知格式,而不是专有模型。 通过社区普遍支持的库为已知格式提供支持。 使用专用格式时,需要特定库。 业务逻辑可能需要处理库提供的部分 API 设计选项。
对于基于架构的格式,请选择在读取器和编写器架构之间进行兼容性检查的编码库。 特定的编码库(如 Apache Avro)要求使用者在反序列化消息之前同时指定编写器和读取器架构。 这项检查可确保使用者知悉架构版本。
互操作性
你选择的格式可能取决于特定的工作负荷或技术生态系统。
例如:
Azure 流分析可为 JSON、CSV 和 Avro 提供本机支持。 当工作负荷使用流分析时,选择以下格式之一是有意义的。
JSON 是用于 HTTP REST API 的标准交换格式。 如果应用程序收到来自客户端的 JSON 有效负载,然后将其放入消息队列进行异步处理,则对消息传送使用 JSON 而不是重新编码为其他格式可能有意义。
这些只是互操作性注意事项的两个示例。 标准化格式通常比自定义格式更具互作性。 在基于文本的选项中,JSON 是最具互操作性的选项之一。
编码格式的选择
以下常用编码格式用于数据表示和传输。 在选择格式之前将各项因素纳入考量。
JSON(JavaScript 对象表示法)
JSON 是一种开放标准,其格式由 RFC 8259 中的 Internet 工程工作队(IETF)定义。 JSON 是一种基于文本的格式,遵循数组/字典/值模型。
JSON 可用于标记元数据,并且无需架构即可分析有效负载。 JSON 支持指定可选字段的选项,这有助于实现向前和向后兼容性。
最大的优势是,它是普遍可用的。 JSON 是许多消息传送服务最可互作的编码格式和默认值。
由于 JSON 是基于文本的格式,因此在传输上效率不高,在存储问题时并不理想。 尽可能使用缩小技术。 如果通过 HTTP 将缓存项直接返回到客户端,则存储 JSON 可能会节省从另一种格式反序列化,然后序列化为 JSON 的成本。
使用 JSON 格式处理单条记录的消息,或处理每个消息具有不同架构的消息序列。 避免针对记录序列使用 JSON,如时序数据。
JSON 还有其他变体,例如二进制 JSON (BSON)。 BSON 是一种与 MongoDB 配合使用的二进制编码。
CSV
CSV 是一种基于文本的表格格式。 表格的标头可以指示字段。 CSV 非常适合包含一组记录的消息。
CSV 的缺点是缺乏标准化。 可通过多种方式表达分隔符、标头和空字段。
协议缓冲区
协议缓冲区(或 protobuf)是一种序列化格式,使用强类型化的定义文件来定义键/值对中的架构。 然后,这些定义文件会被编译为用于对消息进行序列化和反序列化的特定语言类。
该消息包含一个小型压缩的二进制有效负载,从而加快数据传输速度。 但缺点是用户无法读取有效负载。 此外,由于架构存储在外部,因此此格式不适合需要检索存档数据的方案。
Apache Avro
Apache Avro 是二进制序列化格式,它使用类似于 protobuf 的定义文件,但没有编译步骤。 相反,序列化数据始终包含架构报头。
前言可以包含标头或架构标识符。 由于其编码尺寸较小,推荐使用 Avro 进行数据流处理。 此外,因为它有一个适用于一组记录的标头,因此它非常适合表格数据。
Apache Parquet
Apache Parquet 是一种列式存储文件格式,通常与 Apache Hadoop 和相关数据处理框架相关联。
Apache Parquet 支持数据压缩,并且架构演变功能有限。 当工作负荷中的其他大数据技术需要数据创建或使用时,通常会使用此格式。
MessagePack
MessagePack 是一种二进制序列化格式,旨在压缩以通过线路传输。 MessagePack 缺少架构定义和类型检查。 不建议将此格式用于批量存储。
CBOR
CBOR(规范)是一种提供较小编码大小的二进制格式。 使用 CBOR 而非 MessagePack 的优势在于它符合 IETF 的 RFC7049 标准。