跳到主要内容

Exchange 到 Exchange 绑定

·7 分钟阅读
Matthew Sackman

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.bindexchange.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(例如,绑定键为 AliceBill 等)。 整个系统(仅用于在线状态)可能看起来有点像这样

在这里,我们看到 John 与 Alice 和 Bill 是朋友(因此他使用 AliceBill 的路由键从 presence exchange 绑定到他的 queue)。 Alice 和 Bill 彼此不是朋友,但他们每个人都有其他几个朋友,每个朋友都包括 John。

因此,当每个人上线时,他们必须创建他们的 queue,并且他们必须为每个朋友创建到该 queue 的绑定。 在大型聊天系统中,平均朋友数量可能约为 20,并且每分钟可能有数百甚至数千人上线或下线。 此时,绑定中的流失率可能会成为严重的性能瓶颈。 使用 exchange 到 exchange 的绑定,可以解决此问题。 诀窍是允许友谊关系仅通过 exchange 到 exchange 的绑定来表达,即使在用户下线时也可以保留这些绑定。 当用户上线时,他们只需要创建他们的 queue 和一个绑定

与往常一样,路由到没有绑定的 exchange 的消息会消失,因此如果 John 离线,则不会进行缓冲,因此 John_queue 不存在:在这种情况下,exchange John 会丢弃发送给它的所有消息。 因此,结果是,exchange 到 exchange 的绑定网格仅在人们添加朋友或删除朋友时才需要修改,并且朋友上线或下线引起的负载大大降低。

而这仅仅是开始:现在,通过 exchange 到 exchange 的绑定,复杂的路由拓扑成为可能...

© . All rights reserved.