RabbitMQ 中 exchange、route、queue 的关系
从AMQP协议可以看出,MessageQueue、Exchange 和 Binding 构成了 AMQP 协议的核心,下面从应用使用的角度全面的介绍如何利用 Rabbit MQ 构建消息队列以及使用过程中的注意事项。
-
声明 MessageQueue
在RabbitMQ 中,无论是生产者发送消息还是消费这接受消息,都首先需要声明一个 MessageQueue。这就存在一个问题:这个消息队列是生产者声明还是消费这声明?解决这个问题之前需要先明确下面两点:
- 消费者是无法订阅或者获取不存在的 MessageQueue 中消息
- 消息被 Exchange 接受以后,如果没有匹配的Queue,则会被丢弃。
明白上述两点后,就容易理解如果是消费者去声明 Queue,就可能会出现声明 Queue 之前生产者发送的消息被丢弃的隐患。如果应用能够通过消息重发的机制允许消息丢失,则使用次方案没有问题。但如果不允许消息丢弃,这就需要无论是生产者还是消费者,在发送或者接受消息前都需要去尝试建立消息队列。这里还有一点需要明确:如果客户端尝试建立一个已经存在的消息队列,RabbitMQ 不会做任何事情,并返回客户端建立成功。
如果一个消费者在一个信道中在监听某个队列的消息,RabbitMQ 是不允许该消费者在同一个 channel 去声明其他队列的。
RabbitMQ 中可以通过 queue.declare 命令声明一个队列,可以设置该队列以下属性:
- Exclusive: 排他队列,如果体格队列被声明为排他队列,该队列仅对首次声明它的链接可见,并在链接断开时自动删除。这里需要注意三点:
- 排他队列是基于链接可见的,同一链接的不同信道是可以同时访问同一个链接创建的排他队列
- ”首次“,如果一个链接已经声明了一个排他队列,其他链接是不允许建立同名的排队列的,这个与普通队列不同
- 即使该队列是持久化的,一旦链接关闭或者客户端推出,该队列都会被自动删除的,这种队列适用只限于一个客户端发送读取消息的应用场景
- Auto-delete: 自动创建,如果该队列没有任何订阅的消费者,该队列会被自动删除。这种队列适用于临时队列。
- Durable:持久化,这个后面在讲
- 其他选项:例如用户仅仅想查询某一个队列是否已存在,如果存在不想建立该队列,仍然可以调用 queue.declare,只不过需要讲参数 passive 设为 true,传给 queue.declare,如果该队列已存在则会返回 true;如果不存在,则会返回 error,但不会创建新的队列
-
生产者发送的消息
在 AMQP 模型中,Exchange 是接受生产者消息并将消息路由到消息队列的关键组件。ExchangeType 和Binding 决定了消息的路由规则。所以生产者想要发送消息首先必须声明一个 Exchange 和该 Exchange 对应的 Binding。可以通过 ExchangeDeclare 和 BindingDeclare 完成。在 RabbitMQ 中,声明一个 Exchange 需要三个参数:ExchangeName、ExchangeType 和 Durable。ExchangeName 是该 Exchange 的名字,该属性在创建 Binding 和生产者通过 publish 推送消息是需要指定。ExchangeType 是指定 Exchange 的类型,
在 RabbitMQ 中有三种类型的Exchange:direct ,fanout和topic
,exchange的广播类型(交换类型)exchange_type:
- fanout: 所有bind到此exchange的queue都可以接收消息,广播模式
- direct: 通过routing_key决定此exchange的那些匹配的queue可以接收消息,组播模式
- topic:所有匹配routing_key的queue可以接收消息,此时type可以是一个表达式
- headers: 通过headers 来决定把消息发给哪些queue,比较少用。
注意⚠️:
1. 发布者在发布时,如果订阅者是在之后才订阅的,肯定是看不到之前的广播或组播的。广播或组播是实时的,就像现实中的广播和听众一样。
2.如果将消息发送到一个没有队列绑定的exchange上面,那么该消息将会丢失,这是因为在rabbitMQ中exchange不具备存储消息的能力,只有队列具备存储消息的能力。
关于队列、exchange:
无论哪一种广播类型,发布者都无需要声明和指定队列,而是声明和指定exchange,同时指定广播类型。
无论哪一种广播类型,订阅者都需要使用系统随机分配的队列(可以确保唯一性,如果自定义的queue可能重名)来接收消息,并将队列绑定到exchange;同时声明和指定exchange,同时指定广播类型。
关于routing_key:
routing_key,即路由的标识符,可以为任意字符;也可以使用队列名作为标识符,但它本身的含义不是队列名。
不同类型的广播,区别在于exchange_type不同,使得不同类型的routing_key不同。
- fanout类型,发布者无需要指定routing_key。
- direct类型,发布者需要指定routing_key,订阅者使用相同的routing_key匹配,就像分组一样。
- topic类型,发布者需要指定routing_key,订阅者可使用带通配符的routing_key去匹配,是比分组更精细的匹配。
不同的 Exchange 会表现出不同路由行为。Durable 是该 Exchange 的持久化属性。声明一个 Binding 需要提供一个 QueueName、Exc