交换器
什么是交换器?
在 AMQP 0-9-1 中,交换器是 发布者 发布消息的实体,这些消息随后被路由到一组队列或 流。
交换器,就像 AMQP 0-9-1 中的其他拓扑元素一样,由应用程序使用客户端库声明。
交换器的目的是将流经它们的所有消息路由到一个或多个 队列、流 或其他交换器。
交换器的类型和绑定属性用于实现路由逻辑。
交换器属于虚拟主机
与所有其他拓扑元素一样,每个交换器都属于一个 虚拟主机。即使对于仅存在于默认虚拟主机中的系统交换器也是如此。
当创建虚拟主机时,将在其中自动创建许多预先声明的交换器。
交换器名称
每个交换器都必须有一个名称。
一个特殊的交换器,称为默认交换器(见下文),当指定为 发布 目标时,其名称使用空字符串。
在其他上下文中,例如 权限管理,同一个交换器可以被称为 "amq.default"
,但是这个名称不能在发布消息时使用。
交换器类型
交换器可以是不同的类型。交换器类型控制着它如何路由发布给它的消息。例如,一种交换器类型可以使用基于主题(模式)的路由,而另一种可以将所有消息无条件地路由到每个绑定的队列。
RabbitMQ 附带多种交换器类型
- Fanout(扇出):在 教程 3 中介绍
- Topic(主题):在 教程 5 中介绍
- Direct(直连):在 教程 4 中介绍
- 默认直连交换器:一个内置的具有特殊特性的直连交换器
- 本地随机
- JMS 主题
- 一致性哈希交换器
- 随机交换器
- 最近历史交换器
- Headers(头部)
交换器类型可以通过 插件 提供。
Fanout(扇出)
Fanout 交换器将发布给它们的每条消息的副本路由到绑定到它的每个队列、流或交换器。消息的路由键被完全忽略。
请参阅 教程 3,了解如何使用此交换器类型。
Topic(主题)
主题交换器使用 消息的路由键 的模式匹配,来匹配绑定时使用的路由(绑定)键模式。
为了路由的目的,键通过 .
分隔成段。一些段由特定值填充,而另一些段由通配符填充:*
表示恰好一个段,而 #
表示零个或多个(包括多个)段
例如,
"regions.na.cities.*"
的绑定(路由键)模式将匹配消息路由键"regions.na.cities.toronto"
和"regions.na.cities.newyork"
,但 不会 匹配"regions.na.cities"
,因为*
是一个通配符,它匹配恰好一个段"audit.events.#"
的绑定(路由键)模式将匹配"audit.events.users.signup"
和"audit.events.orders.placed"
,但不匹配"audit.users"
,因为第二个段不匹配"#"
的绑定(路由键)模式将匹配任何路由键,并使主题交换器对于使用这种模式的绑定表现得像 fanout 交换器一样
请参阅 教程 5,了解如何使用此交换器类型。
Direct(直连)
直连交换器使用绑定路由键的完全等价性来路由到一个或多个绑定的队列、流或交换器。
例如,"abc"
的绑定(路由)键将只匹配 "abc"
和 "abc"
。
请参阅 教程 4,了解如何使用此交换器类型。
默认交换器
默认交换器是一个直连交换器,它有几个特殊的属性
- 它始终存在(预先声明)
- 对于 AMQP 0-9-1 客户端,它的名称是一个空字符串 (
""
) - 当声明队列时,RabbitMQ 将自动将该队列绑定到默认交换器,使用其(队列)名称作为路由键
这为 AMQP 0-9-1 应用程序提供了一种机制,使其可以方便地仅使用队列名称“直接”发布到队列,即使底层仍然涉及一个直连交换器。
默认交换器用于其特殊属性。它不应该被用作应用程序显式创建绑定的“常规”交换器。
对于需要直连交换器和自定义拓扑的情况,请考虑声明和使用单独的直连交换器。
本地随机交换器
本地随机 是一种专门的交换器类型,在单独的文档指南中介绍。
JMS 主题交换器
JMS 主题 交换器被引入以实现 RabbitMQ JMS 客户端 的某些 JMS 功能。
什么是绑定?
交换器在声明时,并不知道任何队列或流。最初声明的交换器是一个空的命名路由表。
为了用一些路由规则填充该表,队列、流或另一个交换器(见下文)必须 绑定 到该交换器。换句话说,应用程序必须在交换器和队列或流之间创建绑定。
一个绑定具有以下属性
- 源名称:此绑定添加到的交换器的名称
- 目标名称:目标队列、流或另一个交换器的名称
- 目标类型(队列和流为
queue
,交换器到交换器的绑定为exchange
) - 可选的参数映射,某些交换器类型可以使用它(例如,headers 交换器)
绑定持久性
绑定从其源和目标继承持久性。因此,绑定可以是
- 完全持久:它们的交换器和目标/队列/流都是持久的
- 半持久:实际上,这意味着它们的交换器是持久的,而它们的目标/队列/流是瞬态的
- 完全瞬态:它们的交换器和目标/队列/流都是瞬态的
将来会取消对半持久和完全瞬态绑定的支持。
当绑定与瞬态经典队列一起使用时,绑定持久性可能很重要。
当托管非复制(经典)队列的节点停止时,其上的所有瞬态队列和半持久绑定都将被删除。
在大型拓扑的情况下,这可能需要时间,并且当节点重新上线且应用程序重新连接时,半持久和瞬态绑定可能会同时被删除和重新添加,这可能会导致绑定状态不一致和令人困惑的路由行为。
为了避免上述问题,仅使用持久 (复制 或不复制) 队列,可选地使用合理较短的 TTL 和流,并限制瞬态队列的使用,例如,使用默认交换器进行发布。
预先声明的交换器
根据 AMQP 0-9-1 规范
根据 AMQP 0-9-1 规范,每个虚拟主机都包含许多预先声明的交换器
- 默认交换器
- 许多
amq.*
交换器,每种类型一个,例如amq.fanout
或amq.topic
系统交换器
amq.rabbitmq.log
是一个系统主题交换器,由 选择性加入日志记录功能 使用amq.rabbitmq.event
是一个系统主题交换器,由 内部事件机制 使用
交换器属性
交换器有几个关键属性,可以在声明时指定
持久性
就像队列一样,交换器可以是持久的或瞬态的。但是,瞬态交换器在实践中很少使用。
作为经验法则,出于以下原因,请考虑使用持久交换器
- 具有瞬态或客户端特定状态的应用程序很少需要(或使用)自定义交换器,而是依赖于预先声明的交换器(例如
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
是一个系统主题交换器,由 选择性加入日志记录功能 使用amq.rabbitmq.event
是一个系统主题交换器,由内置插件提供,并由 内部事件机制 使用amq.rabbitmq.trace
由 消息跟踪机制 使用
这两种交换器都使得开发自定义日志收集和审计应用程序成为可能,这些应用程序只需要 AMQP 0-9-1 客户端库。