交换器 (Exchanges)
什么是交换机?
在 AMQP 0-9-1 中,交换机是发布者发布消息的实体,然后这些消息会被路由到一系列队列或流。
与 AMQP 0-9-1 中的其他拓扑元素一样,交换机由应用程序使用客户端库声明。
交换机的目的是将所有流经它们的消息路由到一个或多个队列、流或其他交换机。
交换机的类型和绑定属性用于实现路由逻辑。
交换机属于虚拟主机
与其他所有拓扑元素一样,每个交换机都属于一个虚拟主机。即使是只存在于默认虚拟主机中的系统交换机也是如此。
创建虚拟主机时,会自动在其内部创建一些预先声明的交换机。
交换机名称
每个交换机都必须有一个名称。
一个名为默认交换机(见下文)的特殊交换机,在指定为发布目标时,其名称为空字符串("")。
在其他上下文中,例如权限管理中,同一个交换机可以称为 "amq.default",但在发布消息时不能使用此名称。
交换机类型
交换机可以有不同的类型。交换机类型控制它如何路由发布到它的消息。例如,一种交换机类型可以使用基于主题(模式)的路由,而另一种可以无条件地将所有消息路由到每个绑定的队列。
RabbitMQ 提供多种交换机类型
- Fanout:在教程 3 中介绍
- Topic:在教程 5 中介绍
- Direct:在教程 4 中介绍
- 默认直接交换机:具有特殊特性的内置直接交换机
- 本地随机
- JMS 主题
- 一致性哈希交换机
- 随机交换机
- 近期历史交换机
- Headers
交换机类型可以通过插件提供。
Fanout
Fanout 交换机会将发布到它们的每条消息的副本路由到每个绑定的队列、流或交换机。消息的路由键完全被忽略。
请参阅教程 3 了解此交换机类型的用法。
Topic
Topic 交换机使用消息的路由键与绑定时使用的路由(绑定)键模式进行模式匹配。
为了路由目的,键通过 . 分割成段。某些段由特定值填充,而另一些段由通配符填充:* 表示正好一个段,# 表示零个或多个段(包括多个)。
例如,
- 绑定(路由键)模式
"regions.na.cities.*"将匹配消息路由键"regions.na.cities.toronto"和"regions.na.cities.newyork",但**不**匹配"regions.na.cities",因为*是一个精确匹配一个段的通配符。 - 绑定(路由键)模式
"audit.events.#"将匹配"audit.events"、"audit.events.users.signup"和"audit.events.orders.placed",但不匹配"audit.users",因为第二个段不匹配。 - 绑定(路由键)模式
"#"将匹配任何路由键,并使 topic 交换机对于使用该模式的绑定来说,表现得像一个 fanout 交换机。
请参阅教程 5 了解此交换机类型的用法。
Direct
Direct 交换机使用与绑定路由键精确匹配的方式路由到一个或多个绑定的队列、流或交换机。
例如,一个绑定(路由)键 "abc" 将只匹配 "abc"。
请参阅教程 4 了解此交换机类型的用法。
默认交换机
默认交换机是一个具有多个特殊属性的 direct 交换机。
- 它始终存在(已预先声明)。
- AMQP 0-9-1 客户端的名称为空字符串(
"")。 - 当声明一个队列时,RabbitMQ 会自动将该队列绑定到默认交换机,并使用其(队列)名称作为路由键。
这为 AMQP 0-9-1 应用程序提供了一种机制,使其可以通过仅使用其名称“直接”发布到队列,即使底层仍然涉及 direct 交换机。
默认交换机用于其特殊属性。它不应被用作应用程序显式创建绑定的“常规”交换机。
在需要 direct 交换机和自定义拓扑的情况下,请考虑声明并使用单独的 direct 交换机。
本地随机交换机
本地随机是一种专门的交换机类型,将在单独的文档指南中介绍。
JMS Topic 交换机
JMS Topic 交换机是为了实现RabbitMQ JMS 客户端的某些 JMS 功能而引入的。
什么是绑定?
声明的交换机不了解任何队列或流。最初声明的交换机是一个空的命名路由表。
为了用一些路由规则填充该表,必须将队列、流或另一个交换机(见下文)**绑定**到该交换机。换句话说,应用程序必须在交换机与队列或流之间创建绑定。
绑定具有以下属性:
- 源名称:添加到此绑定的交换机的名称
- 目标名称:目标队列、流或另一个交换机的名称
- 目标类型(队列和流为
queue,交换机到交换机的绑定为exchange) - 一个可选的参数映射,某些交换机类型可以使用(例如,headers 交换机)。
绑定持久性
绑定继承其源和目标的持久性。因此,绑定可以是:
- 完全持久:其交换机和目标/队列/流都是持久的。
- 半持久:实际上,这意味着它们的交换机是持久的,而它们的目标/队列/流是临时的。
- 完全临时:其交换机和目标/队列/流都是临时的。
将来将删除对半持久和完全临时绑定的支持。
当绑定与临时经典队列一起使用时,绑定持久性可能很重要。
当托管非复制(经典)队列的节点停止时,其上的所有临时队列和半持久绑定都会被移除。
对于大型拓扑,这可能需要一些时间,并且当节点重新上线且应用程序重新连接时,半持久绑定和临时绑定可能会同时被删除和重新添加,这可能导致绑定的状态不一致和路由行为混乱。
为了避免上述问题,请仅使用持久化(复制或非复制)队列,可选地带有合理的短TTL,以及流,并限制使用临时队列,例如,用于使用默认交换机进行发布。
预声明的交换机
根据 AMQP 0-9-1 规范
根据 AMQP 0-9-1 规范,每个虚拟主机都包含一些预先声明的交换机。
- 默认交换机
- 一系列
amq.*交换机,每种类型一个,例如amq.fanout或amq.topic。
系统交换机
amq.rabbitmq.log是一个系统 topic 交换机,由一个选择加入的日志记录功能使用。amq.rabbitmq.event是一个系统 topic 交换机,由内部事件机制使用。
交换机属性
交换机有几个关键属性可以在声明时指定。
持久性
与队列一样,交换机可以是持久的或临时的。然而,临时交换机在实践中很少使用。
经验法则:考虑使用持久化交换机的原因如下:
- 具有临时或客户端特定状态的应用程序很少需要(或使用)自定义交换机,而是依赖预先声明的交换机(如
amq.topic)。 - 在 4.x 系列中,当 Khepri 成为唯一支持的元数据存储时,将删除对临时(非持久化)实体的支持。
自动删除
自动删除的交换机在其最后一个绑定被移除时将被删除。
这要求至少存在一个这样的绑定(已创建);从未绑定的交换机不会被此机制删除。
可选参数
可选交换机参数,也称为“x-arguments”(因为它们在 AMQP 0-9-1 协议中的字段名称),是一个键/值对的映射(字典),可以在声明交换机时由客户端提供。
该映射由某些功能和交换机类型使用,例如备用交换机和 headers 交换机。
这些可选参数也可以通过策略进行设置。
如果可以通过策略设置可选参数,请始终首先考虑使用策略,而不是将这些值硬编码在应用程序代码中。
可选交换机参数的设置方式可能不同:
- 通过策略分组到交换机(推荐)
- 在声明时按每个交换机进行设置
前者选项更灵活、非侵入性,无需修改和重新部署应用程序。因此,它对大多数用户来说是强烈推荐的。请注意,某些可选参数只能由客户端提供,因为它们不能动态更改,并且必须在声明时已知。然而,这些限制主要适用于队列和流,而不是交换机。
客户端提供可选参数的方式因客户端库而异,但通常是声明交换机函数(方法)的 durable、auto_delete 和其他参数旁边的参数。
可选参数和策略定义的键优先级
当客户端提供的 x-arguments 和策略都提供了相同的键时,前者具有优先权。
但是,如果还使用了操作员策略,那么它将优先于客户端提供的参数。操作员策略是保护机制,会覆盖客户端提供的值和用户策略值。
对于数值,将使用两者中的较小值。如果应用程序需要或选择使用较小的值,操作员策略将允许这样做。然而,高于操作员策略中定义的值将无法使用。
使用操作员策略为与资源使用相关的应用程序控制参数(例如,磁盘空间峰值使用量)引入护栏。
交换机到交换机的绑定
除了队列和流之外,还可以使用 RabbitMQ 中的 AMQP 0-9-1 协议扩展,称为交换机到交换机的绑定(简称 E2E),将一个交换机绑定到另一个交换机。
然后,应用程序发布到源交换机,源交换机将消息路由到目标交换机。
出于效率原因,发布到具有 E2E 绑定的源交换机的消息将仅路由一次,使用源交换机以及所有与其绑定的目标交换机上的所有可用绑定。
换句话说,E2E 绑定不会重新发布消息,它们是路由扩展,尊重源交换机和目标交换机类型的路由。
这意味着目标交换机的入口(入站)消息速率指标不会被更新。目标队列和流的指标会被更新,无论它们是绑定到源交换机还是目标交换机。
备用交换机
备用交换机是一项功能,允许无法路由消息(因为没有合适的绑定)的交换机将路由委托给另一个交换机。
备用交换机在执行拓扑迁移或收集无法路由的消息时很有用。
系统交换机
RabbitMQ 提供了一些内置交换机,用于日志记录和审计目的。
amq.rabbitmq.log是一个系统 topic 交换机,由一个选择加入的日志记录功能使用。amq.rabbitmq.event是一个由内置插件提供的系统 topic 交换机,由内部事件机制使用。amq.rabbitmq.trace由消息跟踪机制使用。
这两个交换机使得开发自定义日志收集和审计应用程序成为可能,而这些应用程序只需要一个 AMQP 0-9-1 客户端库。