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