你当前正在访问 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 还有其他变体,如 BSON,它是与 MongoDB 一致的二进制编码。

逗号分隔值 (CSV)

CSV 是一种基于文本的表格格式。 表格的标头可以指示字段。 当消息包含一组记录时,该格式是首选选项。

其缺点是缺乏标准化。 有很多方式可以用来表示分隔符、标头和空字段。

协议缓冲区 (protobuf)

协议缓冲区(或 protobuf)是一种序列化格式,使用强类型化的定义文件来定义键/值对中的架构。 然后,这些定义文件会被编译为用于对消息进行序列化和反序列化的特定语言类。

消息包含经过压缩的二进制小型有效负载,这可加快传送。 但缺点是用户无法读取有效负载。 此外,由于架构是外部架构,故而不建议在需要检索存档数据的情况下使用。

Apache Avro

Apache Avro 是一种二进制序列化格式,使用与 protobuf 类似的定义文件,但不含编译步骤。 相反,序列化数据始终包含架构报头。

报头可以保留标头或架构标识符。 由于编码较小,建议使用 Avro 流式处理数据。 此外,由于它的标头适用于一组记录,因此对于表格数据,它是一个不错的选择。

MessagePack

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

CBOR

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

后续步骤