Exchange 到 Exchange 绑定
RabbitMQ 2.1.1 版本开始支持 exchange 之间的绑定。 这是 AMQP 规范的扩展,使用此功能(目前)将导致您的应用程序仅能与 RabbitMQ 一起使用,而不能与市面上无数的其他 AMQP 0-9-1 代理实现一起使用。 然而,此扩展极大地提高了路由拓扑的表达性和灵活性,同时解决了一些可伸缩性问题。
正常的绑定允许 exchange 绑定到 queue:发布到 exchange 的消息,如果满足 exchange 及其绑定的各种标准,将通过各种绑定,并在每个绑定的末尾附加到 queue。 这对于许多用例来说很好,但是灵活性很小:它始终只是一跳——消息被发布到一个 exchange,具有一组绑定,因此可能只有一个目标集合。 如果您需要更灵活的东西,那么您将不得不多次发布相同的消息。 使用 exchange 到 exchange 的绑定,一条消息发布一次,可以流经任意数量的 exchange,具有不同的类型,以及比以前可能的更复杂的路由拓扑。
示例:日志记录
想象一个通用的日志记录场景:您想“tap”到 RabbitMQ 内消息流的各个部分,以检查流经该特定 exchange 的消息流。 您无法对 queue 执行此操作,因此最明显的解决方案是添加一个新的 queue,它将成为您的日志记录 queue,并将其绑定到您感兴趣的 exchange。 现在,根据 exchange 的类型和您的绑定键,您可能会收到通过该 exchange 的部分或全部消息。 这可以用下图表示
但是,如果您有多个日志记录 queue 怎么办——您可能有一个用于 syslog,一个用于控制台,一个用于某些第三方管理软件。 如果能够将所有这些都视为单个实体,将会简单得多:因此需要添加一个绑定(如上所述),将所有这些连接到同一个源 exchange。 使用 exchange 到 exchange 的绑定,您现在可以这样做
现在,我们有了现有的日志记录 exchange,其中有几个 queue 接收来自它的所有消息,我们只需要在我们感兴趣的 exchange 和我们的日志记录 exchange 之间添加一个新的绑定(RabbitMQ-orange 中的那个)。 虽然这里的两个 exchange 都是 fanout 类型,但没有必要必须如此:我们可能有不同的日志记录 queue,它们只对流经日志记录 exchange 的消息子集感兴趣。 因此,该 exchange 很可能是一个 topic exchange
所以现在我们有了 syslog 只会接收错误(即路由键以 error.
为前缀的消息),而 console 接收所有消息。 但在这两种情况下,此行为都适用,而与消息的来源无关:日志记录 exchange 可以根据需要绑定到零个、一个或多个 exchange
用法
现有的 queue.bind
AMQP 方法通过其命名表明,您正在执行的操作是将 queue 绑定到 exchange。 这有点令人困惑,因为消息实际上是从 exchange 流出的,通过绑定,然后到达 queue。 但是,简单的是该方法具有用于 queue
名称、exchange
名称和绑定键的字段。
我们引入了 exchange.bind
和 exchange.unbind
AMQP 方法。 遗憾的是,由于此类绑定的两个端点都是 exchange,并且我们不能有两个都称为 exchange
的字段,因此我们不得不提出不同的命名方案。 我们在这里选择反映消息的流动。 因此,字段 source
指示消息进入绑定的 exchange 的名称,字段 destination
指示消息传递到的 exchange 的名称。
我们在我们的 Java、.Net 和 Erlang 客户端中添加了对 exchange 到 exchange 绑定的支持。 我们希望其他社区贡献的客户端也会很快添加支持。
由 exchange.bind
创建的绑定在语义上与 queue.bind
绑定相同:单向的,绑定键和 exchange 类型正常运行,但绑定的两个端点(源和目标)都是 exchange。
与 queue.bind
一样,可以在相同的绑定端点之间创建多个不同的绑定。 我们检测并消除消息传递期间的循环,并确保在任何路由拓扑上,对于给定消息路由到的每个 queue,每个 queue 将只收到该消息的一个副本。 声明为 auto-delete
的 exchange 仍然会在删除所有以该 exchange 为源的绑定时被删除,无论这些绑定的目标是 queue 还是 exchange。 请注意,只有在删除所有以 exchange 为源的绑定时,才会删除自动删除 exchange:如果您添加以给定 exchange 为目标的 exchange 到 exchange 绑定,则在删除这些绑定时,该 exchange 将不会自动删除。 这反映了当删除到 auto-delete
queue 的绑定时,该 queue 不会被删除的事实。
示例 2:在线状态
想象一个聊天系统。 每个用户都将有一个 queue,用于保存发送给该用户的所有消息。 该 queue 还应发送在线状态通知:指示该人的朋友是上线还是下线的事件。
我们虚构的人名叫 John。 当 John 上线时,他将向 presence
exchange 发布一条消息,说明他已在线并可以聊天。 因此,presence
exchange 将是一个 direct
exchange,John 将以 “John” 的 routing key
将他的在线状态发布到该 exchange。 因此,John 的所有朋友都需要订阅 presence
exchange(即,他们需要从该 exchange 绑定到他们自己的 queue),绑定键为 “John”。 登录时,John 自己需要将他的 queue 绑定到 presence
exchange,每个朋友一个绑定:每个绑定都带有不同的 binding key
(例如,绑定键为 Alice
、Bill
等)。 整个系统(仅用于在线状态)可能看起来有点像这样
在这里,我们看到 John 与 Alice 和 Bill 是朋友(因此他使用 Alice
和 Bill
的路由键从 presence exchange 绑定到他的 queue)。 Alice 和 Bill 彼此不是朋友,但他们每个人都有其他几个朋友,每个朋友都包括 John。
因此,当每个人上线时,他们必须创建他们的 queue,并且他们必须为每个朋友创建到该 queue 的绑定。 在大型聊天系统中,平均朋友数量可能约为 20,并且每分钟可能有数百甚至数千人上线或下线。 此时,绑定中的流失率可能会成为严重的性能瓶颈。 使用 exchange 到 exchange 的绑定,可以解决此问题。 诀窍是允许友谊关系仅通过 exchange 到 exchange 的绑定来表达,即使在用户下线时也可以保留这些绑定。 当用户上线时,他们只需要创建他们的 queue 和一个绑定
与往常一样,路由到没有绑定的 exchange 的消息会消失,因此如果 John 离线,则不会进行缓冲,因此 John_queue
不存在:在这种情况下,exchange John
会丢弃发送给它的所有消息。 因此,结果是,exchange 到 exchange 的绑定网格仅在人们添加朋友或删除朋友时才需要修改,并且朋友上线或下线引起的负载大大降低。
而这仅仅是开始:现在,通过 exchange 到 exchange 的绑定,更复杂的路由拓扑成为可能...