AMQP消息属性详解

《深入RabbitMQ》读书笔记

  • 在消息队列中,事件通过消息总线发布到消费者应用程序,每个事件都执行自己特有的任务。但是,如果没有一个标准化的消息格式,我们就难以预测特定的消息类型如何被序列化以及这些消息包含了哪些具体数据。
  • 为了提高消息格式的复用性,AMQP协议的Basic.Properties数据结构提供了一种标准的消息格式,通过AMQP协议发布到RabbitMQ的每条消息都包含这一结构,这使得消费者应用程序可以进行自动反序列化消息,在处理消息之前验证消息的来源以及类型等等。

Basic.Properties的属性

  • content-type 指定消息类型(mime-types)便于序列化/反序列化
  • content-encoding 消息体使用某种特殊的方式进行压缩或者编码
  • message-id 和 correlation-id 唯一标识消息和消息响应,用于实现消息跟踪
  • timestamp 减少消息大小,描述消息创建时间
  • expiration 表明消息过期
  • delivery-mode 在RabbitMQ中表明将消息写入磁盘或者内存队列
  • app-id 和 user-id 帮助追踪出现问题的消息发布者应用程序
  • type 定义发布者和消费者之间的契约
  • reply-to 实现响应消息的路由
  • headers 映射表定义字有格式的属性、实现RabbitMQ路由

本文中对于“契约”的定义:一种确定消息格式和内容的规范。通常用来描述API、对象和系统的预定义规范。契约规范中通常包含有关发送和接收消息的精确信息,例如数据类型、格式以及各种需要遵守的条件。

content-type

  • 通过RabbitMQ发布的消息,我们很容易对它进行复用。例如:最初的消费者应用程序是使用Python编写的,但不久之后,使用PHP、JAVA和C语言编写的程序同样成为的消息的消费者。
  • 当消息格式中没有对消息体内容的描述时,应用程序会倾向使用一种隐式契约,这种隐式契约天生容易出错(例如,Python程序使用pickle序列化的数据无法在其他程序中反序列化),所以你的应用程序非常可能出现问题。
  • 通过指定消息类型,程序员和消费者应用程序不需要猜测如何反序列化消息体中的数据,甚至根本就不需要执行反序列化操作。如果你在消费者代码中使用了一个框架,在消费者代码处理消息之前,通过框架对其进行预处理,消息体可以自动地被反序列化并加载到你所使用的编程语言的本地数据结构中。从而降低消费者应用程序代码的复杂性
tips:
  1. 应该尽量使用标准的序列化格式例如JSON、Msgpack或XML。这些格式允许使用任何编程语言编写任意的消费者应用程序,因为数据是以这些格式进行自我描述的,所以编写潜在的消费者应用程序会很容易。并且在程序外部对消息解码也更简单。
  2. 通过cintent-type属性指定序列化格式,可以更好地支持未来的消费者应用程序-----当消费者可以自动识别它们所支持的消息格式并选择性地处理消息时,那就不必担心在使用新的序列化格式并将其路由到相同的队列时会发生什么情况。

通过gzip和 content-encoding属性压缩消息大小

  • 默认情况下,通过AMQP发送的消息并不会被压缩。
  • 在处理像XML这种过于繁杂的标记语言,甚至在消息数量较大的场景下处理像JSON或YAML等轻量级数据格式时,你的发布者可以在发布消息之前压缩消息,并在收到消息时进行解压缩,就像我们使用gzip在服务器上压缩网页然后在浏览器端实时解压这些网页之后再进行展示一样。
  • 通过与content-type属性相结合,content-encoding属性使消费者应用程序能够基于一种明确的契约与发布者进行交互。你可以编写扩展性更强的代码,确保代码不会由于消息格式变更而导致意外错误。例如:在应用程序的生命周期中,你可能发现bzip2压缩更加适合你的消息内容。如果你在编写消费者应用程序来检查content-encoding属性,则可以拒绝那些不能解码的消息,并把它们留在队列中供其它支持这种解码方式的消费者去消费。

使用message-id和correlation-id引用消息

  • 在AMQP规范中,message-id和correlation-id是“应用级别”的属性,并没有提供正式的行为定义。这就意味着你可以利用它们实现任何目的,这两个字段允许最多255个字节的UTF-8编码数据,并以未压缩的方式存储在Basic.Properties数据结构中。

message-id

  • 某些消息类型(如登录事件)并不需要与其关联的唯一标识,但是订单类型的消息可能需要具备这个唯一标识。当消息流对系统中的各个组件进行耦合时,message-id属性使得消息能够在消息头中携带数据从而唯一地识别该消息。

correlation-id

  • 在AMQP规范中没有关于correlation-id的正式定义,但是通过指定该消息的correlation-id为另一条消息关联消息的message-id,可以指定该消息是另一个消息的响应。另一种用法是使用它来传递关联消息的事务ID或其他类似。

创建事件:timtstamp属性

  • 与message-id和correlation-id一样,timsstamp属性也是“应用级别”的属性。通过timestamp属性来指定消息的创建事件,消费者可以评估消息投递过程的性能、决定是否处理消息、丢弃消息、甚至对应用程序发布警报消息。
tips:
  • 时间戳没有上下文,因此建议在所有消息中使用统一的时区。

expiration:消息自动过期

  • 如果消息没有被消费,expiration概述RabbitMQ何时应该丢弃消息。expiration属性在AMQP的规范定义中比较奇怪:“用于实现,但没有正式的行为”。这意味着RabbitMQ可以提供任何它认为合理的实现方式。同时,expiration的格式是一个短字符串,最多允许255个字符,而代表时间单位的另一个属性timstamp则是一个整数值。
  • 由于规范中没有给出明确说明,当使用不同的消息代理服务器甚至同一消息代理服务器的不同版本时,expiration可能会有不同的含义。想要利用expiretion属性来实现RabbitMQ消息的自动过期,必须把一个UNIX时间戳存储为字符串。
  • 使用expiration属性时,如果把一个已经过期的消息发布到服务器,那么这条消息不会被路由到任何队列,而是直接被丢弃。
tips:
  • RabbitMQ3.0以上才支持expiration属性。

使用delvery-mode平衡速度和安全性

  • delivery-mode属性是一个字节字段,像消息代理服务器表明在将消息投递到任何正在等待的消费者之前,你希望先将它持久化到磁盘上。delivery-mode属性有两个可能的值:1 代表非持久化消息,2 代表持久化消息。
  • 消息的持久性与队列的持久性(durable)
    • 队列的持久性(durable)告诉RabbitMQ该队列在重新启动RabbitMQ服务器或集群之后是否仍然有效。
    • 只有消息的delivery-mode为2时,才会向RabbitMQ指定消息是否应该被持久化。
    • 一个队列可能包含持久化和为持久化的消息

使用app-id和user-id验证消息来源

  • app-id和user-id属性提供了关于消息的另一层信息,并且有很多潜在的用途

app-id

  • app-id属性在AMQP规范中定义为“短字符串”,最多允许255个UTF8字符,如果应用程序采用的时以带版本的API为中心的设计,那么在生成消息时可以使用app-id传递特定的版本号,在处理消息之前检查app-id允许应用程序丢弃那些来源不明或者不受支持的消息
  • app-id的另一个属性是收集统计数据。例如,如果你使用消息来传递登录事件,则可以将app-id设置为触发登录事件的平台和应用程序版本。在一个需要同时支持web端、桌面端和移动端应用的环境中,如果希望跟踪并统计各个平台的登录数据,使用这种方式我们甚至不需要检查消息体。
  • 如果一个新的消息发布者错误地使用了与现有发布者应用程序相同的Exchange和routing_key时,通过app-id可以更容易地追踪恶意消息的来源

user-id

  • 在需要验证用户身份时,可以使用user-id属性来标识已登录的用户。但大多数情况下,并不推荐这种做法。RabbitMQ会根据发布消息的RabbitMQ用户信息检擦每条已发布消息的user-id属性值,如果这两个值不匹配,那么该消息会被拒绝。

使用type属性获取明细

  • AMQP规范的0-9-1版本将type属性定义为“消息类型名称”,它用来描述消息中的内容。
  • 像JSON和XML这样的自描述格式被一些人认为太冗长了。它们可能在网络传输或者内存存储上带来不必要的开销,序列化和反序列化相较一些语言也比较慢。当消息体没有以自描述格式进行序列化(例如Apache Thrift、ProtoBuf这样的序列化格式),这些二进制编码的消息格式不是自描述的,需要依赖外部定义的文件来进行序列化和反序列化。这时,可以通过type属性指定记录类型或者外部定义文件,如果无法正确访问处理消息所需的.thrift或.proto文件,消费者就能够拒绝这些消息。

使用reply-to实现动态工作流

  • AMQP规范中,reply-to属性被指定用于应用程序,但是没有规定的行为,他还有一个附加说明:使用reply-to可以构架一个用来回复消息的私有响应队列。
  • 尽管在AMQP规范中没有说明私有响应队列的确切定义,但是该属性可以在最初发布消息的相同Exchange中携带特定的队列名称或者routing_key,这些队列名称或者routing_key可以用于回复消息。

使用消息头定义头属性

  • headers是一个键值对映射表,允许用户自定义任何的key/value。键可以是ASCII或者Unicode字符串,最大长度为255个字符。值可以是任何有效的AMQP值类型。
  • headers属性允许添加任何你想要的数据到消息头中。除此之外,它还具有另一个独特的功能:RabbitMQ可以根据headers表中填充值来进行消息的路由,而不需要依赖于routing_key

优先级属性

  • 截至3.5.0版本,RabbitMQ已经按照AMQP规范实现了priority字段,它的取值范围是一个介于0~9之间的整数,用于指定队列中消息的优先级。
  • 如果首先发布一条优先级为9的消息,随后再发布一条优先级为0的消息,则新连接的消费者将会先接收到优先级为0的消息
tips:
  • RabbitMQ将priority字段实现为无符号字节,所以优先级可以是0到255之间的任意值,但最好将取值范围限制在0到9之间以保证规范性。

不能使用的属性: cluster-id/reserved

  • cluster-id属性是AMQP 0-8中定义的,但随后被删除,RabbitMQ从未实现过关于改属性的任何行为
  • AMQP 0-9-1将cluster-id属性重新命名为reserved,并声明它必须为空,虽然RabbitMQ目前没有根据规范要求它是空的,但是最好规避这个属性。

总结

属性 类型 用途 使用建议或特殊用法
app-id short-string 应用程序 用于发布消息的应用程序
content-encoding short-string 应用程序 指定消息体是否以某种特殊方式编码,如zlib、deflate或Base64
content-type short-string 应用程序 使用mime-types指定消息体的类型
correlation-id short-string 应用程序 如果消息引用了某个其他消息或具有唯一标识的项目,那么correlation-id可以用来指定这种引用关系
delivery-mode octet RabbitMQ 值为1告诉RabbitMQ可以将消息保存在内存中;值为2表示它也应该被写入磁盘
expiration short-string RabbitMQ 用文本字符串表示的纪元时间或者UNIX时间戳,表示消息的过期时间
headers table 应用程序/RabbitMQ 一个自由格式的键值表,可以使用它来添加消息相关的附加元数据;RabbitMQ也可以基于它进行路由
message-id short-string 应用程序 唯一的标识符,例如在应用程序中可以使用uuid来标识消息
priority octet RabbitMQ 队列中标识消息的优先顺序
timestamp timestamp 应用程序 用文本字符串表示的纪元时间或者UNIX时间戳,表示消息的创建时间
type short-string 应用程序 一个文本字符串,用于表示应用程序中描述消息或有效负载的类型
user-id short-string 应用程序/RabbitMQ 一个自由格式的字符串,如果启用该属性,RabbitMQ会验证当前连接的用户,若不匹配则丢弃消息

你可能感兴趣的:(AMQP消息属性详解)