消息、有效负载和序列化

Azure 服务总线处理消息。 消息中承载着有效负载和元数据。 元数据采用键值对的形式。 它描述了数据负载,并提供有关如何在服务总线和应用程序中处理的说明。 有时,仅此元数据就足以携带发送方希望与接收方通信的信息,因此有效负载保持为空。

用于.NET和Java的官方服务总线客户端的对象模型反映了抽象服务总线消息结构。 此结构映射到服务总线支持的线路协议和网络协议。

服务总线消息由一个二进制有效负载部分和两组属性组成,其中二进制有效负载在服务端不会以任何形式处理。 代理属性由系统预定义。 这些预定义属性要么控制中转站内的消息级功能,要么映射到常见的标准化元数据项。 用户属性是应用程序可以定义和设置的键值对的集合。

下表列出了预定义的代理属性。 所有官方客户端 API 都使用这些名称。 BrokerProperties HTTP 协议映射的 JSON 对象也使用这些名称。

在高级消息队列协议 (AMQP) 协议级别中使用的等效名称会在括号中列出。 虽然以下名称使用帕斯卡命名法,但 JavaScript 和 Python 客户端分别使用驼峰命名法和蛇形命名法。

属性名称 说明
ContentType(content-type) 视需要描述消息的有效负载,采用符合 RFC2045 第 5 部分格式的描述符;例如,application/json。
CorrelationId(correlation-id) 使应用程序能够为消息指定上下文以实现关联;例如,反映正在答复的消息的 MessageId 。
DeadLetterSource 仅在死信消息中设置,以后从死信队列自动转发到另一个实体。 指明已成为死信的消息所在的实体。 此属性为只读。
DeliveryCount

系统尝试传送该消息的次数。 当消息锁过期或接收方显式放弃消息时,计数会递增。 此属性为只读。

当基础 AMQP 连接关闭时,传递计数不会递增。

EnqueuedSequenceNumber 对于系统自动转发的消息,此属性反映系统首先在其原始提交点分配给邮件的序列号。 此属性为只读。
EnqueuedTimeUtc 消息被接受并存储在实体中的 UTC 时刻。 当接收方不想信任发送方的时钟时,使用此值作为权威和中性到达时间指示器。 此属性为只读。
Expires​AtUtc(absolute-expiry-time) 消息被标记为将在其过期后删除的 UTC 时间点,从该实体便无法再检索。 到期由 TimeToLive 属性控制。 系统从 EnqueuedTimeUtc+TimeToLive 计算此属性。 此属性为只读。
Label 或 Subject (subject) 借助此属性,应用程序可以类似于电子邮件主题行的标准化方式,向接收程序指明消息目的。
Locked​Until​Utc 对于在锁定(速览锁定接收模式(未预设)下检索的消息,此属性反映 UTC 即时,直到消息被锁定在队列或订阅中。 锁定过期后, DeliveryCount 会递增,消息将再次可供检索。 此属性为只读。
Lock​Token 锁令牌是对代理在 速览锁 接收模式下持有的锁的引用。 使用令牌通过 Deferral API 永久固定锁,并使用该令牌将消息从常规传递状态流中取出。 此属性为只读。
Message​Id(message-id) 消息标识符是应用程序定义的值,可用于唯一标识消息及其有效负载。 此标识符是自由格式字符串,可反映 GUID 或派生自应用程序上下文的标识符。 启用后,重复检测功能会识别并删除具有相同MessageId的再次和重复提交的消息。
Partition​Key 对于已分区实体,设置此值后,可以将相关消息分配到同一内部分区,以便能够正确记录提交序列顺序。 分区是由哈希函数通过此值进行选择,无法直接选择。 对于会话感知实体,SessionId 属性替代此值。
Reply​To(reply-to) 应用程序定义的这一可选值是一种标准方法,可用于向消息接收程序明示答复路径。 如果发送程序希望收到答复,它会将此值设置为,要将答复发送到的队列或主题的绝对或相关路径。
Reply​To​Session​Id(reply-to-group-id) 此值补充了 ReplyTo 信息,并指定了应为发送给答复实体的答复设置的 SessionId。
Scheduled​Enqueue​Time​Utc 对于仅在延迟后才可供检索的消息,此属性定义了 UTC 时间点,该时间点表示消息将按逻辑排入队列、进行排序,并因此可供检索。
Sequence​Number 序列号是分配给消息的唯一 64 位整数,因为中转站接受并存储它。 它充当消息的真实标识符。 对于已分区实体,最前面的 16 位数反映的是分区标识符。 序列号单调递增且无间隔。 在 48-64 位范围用尽后,它们会回滚到 0。 此属性为只读。
Session​Id(group-id) 对于会话感知实体,应用程序定义的此值指定了消息的会话附属关系。 会话标识符相同的消息会处于摘要锁定状态,并确切启用依序处理和解多路复用。 对于非会话感知实体,将忽略此值。
Time​To​Live 此值是消息过期的相对持续时间,从中转站接受并存储消息的时间开始,如 EnqueueTimeUtc 中捕获的那样。 如果不显式设置此值,系统会假定该队列或主题的DefaultTimeToLive。 消息级别 TimeToLive 的值不得超过实体的 DefaultTimeToLive 设置。 如果超过限定,系统会在不通知的情况下进行调整。
To(to) 此属性保留供将来在路由方案中使用,代理当前将忽略它。 应用程序可以在规则驱动的自动转发链方案中使用此值,以指明消息的预期逻辑目标。
Via​Partition​Key 如果消息是通过事务范围内的传输队列发送,此值选择传输队列分区。

抽象消息模型使消息能够通过 HTTPS 发布到队列,并通过 AMQP 进行检索。 无论属于上述哪种情况,在相应协议的上下文中,消息看上去都很正常。 系统根据需要转换代理属性。 用户属性映射到相应协议消息模型中最合适的位置。 在 HTTP 中,用户属性直接与 HTTP 标头相互映射。 在 AMQP 中,它们映射到 application-properties 和从 application-properties 映射。

消息路由和关联

前面描述的中转站属性的子集,特别是To、ReplyTo、ReplyToSessionId、MessageIdCorrelationId和SessionId,帮助应用程序将消息路由到特定目标。 为了说明这个功能,假设有以下几种模式:

  • 简单请求/答复:发布者向队列发送消息,并希望收到消息使用者的答复。 为了接收答复,发布者拥有答复预期传递到的队列。 此队列的地址将通过出站消息的 ReplyTo 属性表示。 如果使用者响应,它会将已处理消息的 MessageId 复制到答复消息的 CorrelationId 属性,并将消息传递到 ReplyTo 属性指明的目标。 一个消息可以有多个答复,具体视应用程序上下文而定。
  • 多播请求/回复:作为上一个模式的一种变体,发布者将消息发送到主题中,多个订阅者有资格使用该消息。 每个订阅者都可能会以先前描述的方式响应。 此模式用于发现或滚动呼叫应用场景,并且回应者通常通过用户属性或在有效负载内标识其自身。 如果 ReplyTo 指向主题,可以向受众分发这样的一组发现响应。
  • 多路复用:借助此会话功能,可以通过一个队列或订阅对相关消息流进行多路复用,以便相关消息的所有会话/组(与 SessionId 值匹配)可以路由到特定接收程序,同时接收程序能够使会话保持锁定状态。 若要了解更多有关会话的详情,请点击此处。
  • 多路复用请求/答复:借助此会话功能,可以启用多路复用答复,让多个发布者能够共享答复队列。 通过设置 ReplyToSessionId,发布者可以指示使用者将此值复制到答复消息的 SessionId 属性。 发布队列或主题不需要是会话感知的。 当消息发送后,发布者可以专门等待具有给定SessionId的会话在队列中具体化,然后再有条件地接受会话接收器。

可以使用自动前向链接和主题订阅规则实现服务总线命名空间内的路由。 可以在使用 Azure LogicApps 实现跨命名空间的路由。 如上面的列表所示,已保留 To 属性以供将来使用,可能最终会被已启用特别功能的代理解析。 要实现路由的应用程序应基于用户属性执行此作,而不是依赖于 To 属性;但是,现在这样做不会导致兼容性问题。

负载序列化

在传输中或存储在服务总线内时,有效负载始终是一个不透明的二进制块。 使用 ContentType 属性,应用程序可以描述有效负载,对属性值采用符合 IETF RFC2045 MIME 内容类型描述的建议格式;例如,application/json;charset=utf-8。

选择显式序列化格式,如JSON,并在发送端和接收端使用相同格式。 在支持的 .NET 库中,ServiceBusMessage.Body 使用 BinaryData. Java库还提供BinaryData消息体。 这些类型允许你的应用程序处理字节、字符串和序列化对象。

为了实现跨语言和协议的互操作性,发送应用对象前应序列化,并将 ContentType 设置为用于描述该格式。 在接收端使用该格式对消息体进行反序列化。

注意

旧 WindowsAzure.ServiceBus 库发送的现有消息可以包含由 DataContractSerializer 序列化的正文,或者编码为 AMQP 值的正文。 在迁移过程中保持与这些消息的兼容性。 详见AMQP序列化细节和.NET互操作性示例。

处理消息有效负载中的对象反序列化时,开发人员应考虑消息可能使用不同的序列化方法从多个源到达。 在不断演变单个应用程序时,这种情况也可能发生,旧版本将继续与较新版本一起运行。 在这些情况下,请考虑当第一次反序列化失败时,尝试使用其他反序列化方法。 支持此方法的一个库是 NServiceBus。 如果所有反序列化方法都失败,则将消息放入死信队列。

后续步骤

若要了解有关服务总线消息传送的详细信息,请参阅以下主题: