RabbitMQ 消息路由模式/交换器
《RabbitMQ系列》: 1 . RabbitMQ 与 AMQP 协议
2 . RabbitMQ 消息属性详解 3. RabbitMQ 可靠性与性能的平衡
4. RabbitMQ 消费消息
消息路由模式
RabbitMQ
最强大之处就在于其灵活性:它能根据消息发布方提供的路由信息,将消息路由到不同的队列中。不论是将消息发往单个队列、多个队列、交换器,还是另一个由交换器插件提供的外部源,RabbitMQ
的路由引擎能始终保持极快的速度和灵活性。
通过 direct 交换器路由消息
当需要投递的消息有一个确定的目标(或者多个目标)时,direct
交换器就能派上用场。任何绑定在交换器上的队列,只要它的路由键和发布消息时的一致,它就能收到消息。
对于 direct
交换器来说,RabbitMQ
在检查绑定时会比较字符串是否相等。此时不允许使用任何类型的模式匹配。
如下图,使用了 direct
交换器,消息发布者1发布的消息将会路由至队列1和队列2,而消息发布者2发布的消息将会路由至队列2和队列3中:
RabbitMQ
内置了 direct
交换器,所以不需要额外的插件。要创建一个 direct
交换器非常简单,只需要将其声明为 direct
即可。
它非常适用于 RPC
消息通信模式下的路由应答消息。对于那些需要使用由多台服务器提供的不同组件的应用来说,使用 RPC
来解除应用耦合正是实现高度可扩展性的良方。
通过 fanout 交换器广播消息
所有发往 fanout
交换器的消息会被投递到所有绑定到该交换器上的队列中。由于 RabbitMQ
不需要在投递消息时检测路由键,这将带来可观的性能优势。
fanout
交换器提供了非常棒的方式,让每一个消费者都能访问到原始数据。不过这也是一把双刃剑,因为消费者无法对收到的消息进行选择。
使用 topic
交换器有选择地路由消息
topic
交换器会将消息路由至匹配路由键的任一队列中。通过采用句点分隔的形式,队列可以通过使用基于通配符的模式匹配的方式来绑定到路由键上。通过使用星号(*
)和井号(#
)字符,可以在同一时刻匹配路由键的特定部分,甚至是多个部分。星号将会匹配路由键中下一个句点前的所有字符,而井号键将会匹配接下来所有的字符,包括句点。
下图展示了由三部分组成的 topic
交换器路由键:
topic
交换器是消息路由的极佳选择,它使得单一用途的消费者能够对消息进行不同的处理。如下图,基于路由键的构造方式,消息会被有选择地路由到不同的队列中去:
利用这种体系结构的单一用途的消费者可以更容易维护和扩展。
注意:创建富含消息语义的路由键很有用。它能够描述消息的意图或内容。相对于基于应用程序的细节来设计消息及其路由键来说,基于事件的通用消息通信则鼓励消息的可重用性。当开发人员能够在应用程序中复用现有消息时,代码复杂性降低以及消息吞吐量降低是最显而易见的优势。
当进行路由键的的完全匹配时,topic
和 direct
交换器之间几乎没有差异。但是,通过使用 topic
交换器,将能对路由键进行部分模式匹配,将来若有其他用途时也不必改动现有的消息通信架构。
使用 headers 交换器有选择地路由消息
headers
交换器通过采用消息属性中的 headers
表支持任意的路由策略。绑定至 headers
交换器的队列会向 Queue.Bind
参数中传入键值对数组以及 x-match
参数。x-match
参数是字符串类型,可以设置为 any
或者 all
。如果将其设置为 any
,同时 headers
表中的值匹配了任何一个绑定值的话,消息就会被路由过去。如果将 x-match
设置为 all
的话 ,那么所有传入 Queue.Bind
中的参数值必须全部匹配才行。这并不排除消息在 headers
表中拥有额外的键值对。
尽管 headers
交换器通过 any
和 all
匹配能力确实增添了额外的灵活性,但同时也带来了额外的路由计算开销。在使用 headers
交换器路由消息时,headers
属性中的所有值必须在计算值之前按照键的名称进行排序。传统观点认为,由于额外的计算复杂性,headers
交换器比其他交换器类型要慢得多。
交换器间绑定
RabbitMQ
团队往 RabbitMQ
中添加了一种非常灵活的机制(未包含在 AMQP
规范中),允许将消息路由至交换器的任意组合。从而使得应用程序可以使用交换器的一部分功能加上另一种交换器的另一部分功能。
交换器间的绑定类似于队列绑定,与将队列绑定至交换器上不同的是,需要使用 RPC
方法 Exchange.Bind
将一个交换器绑定至另一个交换器上。
当使用交换器间绑定时,绑定交换器的路由逻辑和绑定队列是一致的。任何交换器都可以绑定到任何一个内建类型的交换器上。
作为一种工具,交换器间绑定为消息通信模式带来了巨大的灵活性。但是这一灵活性也伴随着额外的复杂性和开销。
使用一致性哈希交换器路由消息
一致性哈希交换器(consistent-hashing exchange
)插件随着 RabbitMQ
一同发布,将数据分发给绑定的队列上。可为用于接收消息的队列做负载均衡。
相比 RabbitMQ
将消息分发至单个队列的多个消费者来说,它提供了潜在的更为快速的吞吐量。当使用数据库或其他系统以消费者的身份直接集成至 RabbitMQ
上时,一致性哈希交换器提供了扩展数据的能力而无须编写中间件。
注意
如果考虑使用一致性哈希交换器来提升消费者吞吐量的话,应当为以下两种场景建立基准测试并加以区分:单队列多消费者和仅有单个消费者的多个队列。这样一来就能辨别出哪一种选择更合适。
一致性哈希交换器采用一致性哈希算法来决定哪个队列将会收到消息。不同于将队列绑定至路由键或者头信息,队列将被绑定至一个基于整型的权重值。算法的一部分采用该权重值来决定消息应如何投递。假设有两个队列绑定在了一致性哈希交换器上,并且这两个队列由相同的权重,那么消息的分发会接近于五五开。
一致性哈希交换器不会轮询(round-robin
)消息,而是基于路由键或消息属性中 header-type
值的哈希值来作出明确的路由。不过,相较而言,拥有更高权重的队列将能从交换器接收到更高比例的消息。
值得注意的是,由于一致性哈希算法的工作方式,如果变更了绑定至交换器的队列总数的话,那么消息的分布极有可能会跟着发生变化。
记得 转发 、 在看 、 关注 哦!