你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

消息编码注意事项

许多云应用程序使用异步消息在系统组件之间交换信息。 消息传送的一个重要方面是用于对有效负载数据进行编码的格式。 在选择消息传送技术后,下一步便是定义消息的编码方式。 虽有多个选项可供选择,但正确的选择取决于用例。

本文描述了部分注意事项。

消息交换需求

在生成者和使用者之间交换消息需要:

  • 可以定义消息的有效负载的形状或结构。
  • 用于表示有效负载的编码格式。
  • 用于读取和写入编码负载的序列化库。

消息的生成者可以根据业务逻辑和要发送给使用者的信息来定义消息形状。 若要构建形状,需将信息分割成不同或相关的主题(字段)。 确定这些字段的值的特征。 需要考虑以下问题。

  • 什么是最有效的数据类型?
  • 有效负载是否始终包含某些字段?
  • 有效负载是否具有单个记录或重复的值集?

然后,根据需要选择一种编码格式。 某些因素包括根据需要创建高度结构化数据的能力、编码和传送消息所用的时间,以及分析有效负载的能力。 根据编码格式,选择一个最合适的序列化库。

消息的使用者必须了解这些决策,以便知道如何读取传入消息。

若要传送消息,生成者需将消息序列化为某种编码格式。 在接收端,使用者需对有效负载进行反序列化,方可使用数据。 两个实体可以通过这种方式共享模型,并且只要形状不变,消息传送便会继续进行且不会出现问题。 但当协定发生变化时,编码格式应能够应对变化,而且不会中断使用者。

某些编码格式(如 JSON)为自述性,这意味着可以在不引用架构的情况下对其进行分析。 但是,此类格式往往会生成较大的消息。 使用其他格式时,数据可能不太容易分析,但消息很紧凑。 本文重点介绍有助于选择格式的一些因素。

编码格式注意事项

编码格式定义如何以字节形式表示一组结构化数据。 消息类型可能会影响格式选择。 与业务事务相关的消息最有可能包含高度结构化的数据。 此外,你可能希望稍后检索它以进行审核。 对于事件流,可能希望尽快读取记录序列,并将其存储起来以供统计分析。

下面是在选择编码格式时要考虑的一些要点。

用户可读性

消息编码可以大致分为基于文本的格式和二进制格式。

使用基于文本的编码,消息有效负载采用纯文本形式,因此人可以无需使用任何代码库即可进行检查。 用户可读格式适用于存档数据。 此外,用户可以读取有效负载,因此,基于文本的格式更易调试并发送到日志,以便对错误进行故障排除。

缺点是有效负载往往更大。 数据负载大小通常可以通过代码压缩过程来减少,前提是可以在需要时恢复以便于人工阅读。 基于文本的常见格式为 JSON 和 YAML。

加密

如果消息中存在敏感数据,请考虑是否应按照本指南中关于加密 Azure 服务总线静态数据的说明对这些消息进行整体加密。 或者,如果只需加密某些字段,并且希望降低云成本,请考虑使用 NServiceBus 等库。

编码大小

消息大小会影响整个网络的网络 I/O 性能。 二进制格式较基于文本的格式更简洁。 但二进制格式需要序列化/反序列化库。 除非对有效载荷进行解码,否则无法读取。

如需减少网络占用情况并更快地传送消息,请使用二进制格式。 在需要考虑存储或网络带宽的情况下,建议使用此类格式。 二进制格式选项包括 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。

    如果使用不同的架构对消息进行单独编码,则此布局适用。 如有多条记录,则有效负载可能会过度冗余,从而导致有效负载膨胀。

  • 表格数据:信息分为行和列。 每列表示一个字段或信息的主题,而每行则包含这些字段的值。 此布局对于一组重复的信息(如时序数据)十分有效。

    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 进行流式数据处理。 此外,由于它带有适用于一组记录的标题,因此它是处理表格数据的理想选择。

Apache Parquet

Apache Parquet 是一种列式存储文件格式,通常与 Apache Hadoop 和相关数据处理框架相关联。

该格式支持数据压缩,并且具有一些有限的功能来支持架构演变。 这种格式是你可能只会使用的一个示例,因为工作负荷中的其他大数据技术选择将负责创建或使用这些数据。

MessagePack

MessagePack 是旨在用于进行压缩以进行网络传输的二进制序列化格式。 没有消息架构或消息类型检查。 不建议将此格式用于批量存储。

CBOR

简明二进制对象展现 (CBOR)(规范)是一种二进制格式,可提供较小的编码大小。 CBOR 相较于 MessagePack 的优势在于其符合 IETF 的标准,该标准在 RFC7049 中进行了规定。

后续步骤