整理RabbitMQ介绍

RabbitMQ介绍

使用Erlang语言实现的消息中间件,用于在分布式系统中存储转发消息。

RabbitMQ模型

RabbitMQ 整体上是一个生产者与消费者模型,主要负责接收、存储和转发消息。可以把消息传递的过程想象成:当你将一个包裹送到邮局,邮局会暂存并最终将邮件通过邮递员送到收件人的手上,RabbitMQ就好比由邮局、邮箱和邮递员组成的一个系统。从计算机术语层面来说,RabbitMQ 模型更像是一种交换机模型。

生产者与消费者

生产者 Producer

生产消息的一方

消费者 Consumer

使用消息的一方

交换机

交换机 Exchange

交换机接收生产者发送的消息并将这些消息路由到队列中。

在RabbitMQ中,消息并不是直接被投递到消息队列中的,中间还必须经过交换机这一层,交换机会把我们的消息分配到对应的消息队列中。

交换机有四种类型,不同类型对应不同的路由策略。

direct、fanout、topic、headers

路由键、绑定键

生产者发消息给交换机的时候,一般会指定一个路由键 RoutingKey,用来指定这个消息的路由规则,而这个RoutingKey需要与交换机类型和绑定键 BindingKey联合使用才能最终生效。

RabbitMQ中通过 绑定 Binding 将交换机和消息队列关联起来,在绑定的时候一般会指定一个绑定键,这样RabbitMQ就知道如何正确将消息路由到队列了。如下图:

一个绑定就是基于路由键将交换器和消息队列连接起来的路由规则,所以可以将交换器理解成一个由绑定构成的路由表。Exchange 和 Queue 的绑定可以是多对多的关系。

生产者将消息发送给交换器时,需要一个RoutingKey,当 BindingKey 和 RoutingKey 相匹配时,消息会被路由到对应的队列中。在绑定多个队列到同一个交换器的时候,这些绑定允许使用相同的 BindingKey。BindingKey 并不是在所有的情况下都生效,它依赖于交换器类型,比如fanout类型的交换器就会无视,而是将消息路由到所有绑定到该交换器的队列中。

消息队列

消息队列Queue 用来保存消息,直到消息发送给消费者。

消息队列是消息的容器,一个消息可投入一个或多个队列中。

RabbitMQ中,消息只能存储在队列中,这一点和Kafka相反。Kafka将消息存储在 topic(主题) 这个逻辑层面,而相对应的队列逻辑只是topic实际存储文件中的位移标识。

多个消费者可以订阅一个队列,队列中的消息会被轮询分配(Round-Robin)给多个消费者进行处理,而不是每个消费者都收到所有的消息并处理,这样可以避免消息被重复消费。

RabbitMQ不支持队列层面的广播消费。

Broker服务节点

一个RabbitMQ Broker可以简单地看作一个 RabbitMQ 服务节点,或者RabbitMQ服务实例。大多数情况下也可以将一个RabbitMQ Broker看作一台 RabbitMQ 服务器。

下图展示了生产者将消息存入 RabbitMQ Broker,以及消费者从Broker中消费数据的整个流程。

交换机类型

direct

不指定交换机类型时,默认位direct。

路由规则:把消息路由到那些 Bindingkey 与 RoutingKey 完全匹配的 Queue中。如下图:

如果发送消息的时候设置路由键为“warning”,那么消息会路由到 Queue1 和 Queue2。
如果发送消息的时候设置路由键为"Info”或者"debug”,消息只会路由到Queue2。
如果以其他的路由键发送消息,则消息不会路由到这两个队列中。

direct 类型常用在处理有优先级的任务,根据任务的优先级把消息发送到对应的队列,这样可以指派更多的资源去处理高优先级的队列。

fanout

路由规则:

把所有发送到该 Exchange 的消息路由到所有与它绑定的 Queue 中,不需要做任何判断操作,所以 fanout 类型是所有的交换机类型中速度最快的。fanout 类型常用来广播消息。

topic

前面讲到direct类型的交换机路由规则是完全匹配路由键和绑定键的,但这种严格的匹配方式在很多情况下不能满足实际业务需求。topic类型的交换机在匹配规则上进行了扩展,它约定:

  • RoutingKey 为一个点号“ . ”分隔的字符串(被点号“ . ”分隔开的每一段独立的字符串称为一个单词),如 “com.rabbitmq.client”、“java.util.concurrent”、“com.hidden.client”;
  • BindingKey 和 RoutingKey 一样也是点号“ . ”分隔的字符串;
  • BindingKey 中可以存在两种特殊字符串“”和“#”,用于做模糊匹配,其中“”用于匹配一个单词,“#”用于匹配多个单词(可以是零个)

以上图为例:

  • 路由键为 “com.rabbitmq.client” 的消息会同时路由到 Queuel 和 Queue2;
  • 路由键为 “com.hidden.client” 的消息只会路由到 Queue2 中;
  • 路由键为 “com.hidden.demo” 的消息只会路由到 Queue2 中;
  • 路由键为 “java.rabbitmq.demo” 的消息只会路由到Queuel中;
  • 路由键为 “java.util.concurrent” 的消息将会被丢弃或者返回给生产者(需要设置 mandatory 参数),因为它没有匹配任何路由键。

headers(不推荐使用)

headers 类型的交换机不依赖于路由键的匹配规则来路由消息,而是根据发送的消息内容中的 headers 属性进行匹配。

在绑定队列和交换机时制定一组键值对,当发送消息到交换机时,RabbitMQ会获取到该消息的 headers(也是一个键值对的形式)对比其中的键值对是否完全匹配队列和交换机绑定时指定的键值对,如果完全匹配则消息会路由到该队列,否则不会路由到该队列。headers 类型的交换机性能会很差,而且也不实用,基本上不会看到它的存在。

死信队列

顾名思义,就是消息队列中的消息因为某些原因导致无法被消费,这样的消息没有后续的处理,为了处理这些死信消息,就有了死信队列。

应用场景:为了保证消息数据不丢失,当消息发生异常时,将消息放入死信队列中。比如订单系统,当订单在支付页面指定时间内没有付款,订单过期,但为了保证订单信息不丢失,放入死信队列中。

死信的来源

消息TTL过期

TTL是Time To Live的缩写,也就是消息的生存时间

队列达到最大长度

队列满了,无法放入消息到MQ中

消息被拒绝

(basic.reject 或 basic.nack) 并且 requeue=false

七种队列模式

简单模式

一个生产者对应一个消费者,RabbitMQ相当于一个消息代理,负责将A的消息转发给B
应用场景:将发送的电子邮件放到消息队列,然后邮件服务在队列中获取邮件并发送给收件人

工作队列模式

在多个消费者之间分配任务(竞争的消费者模式),一个生产者对应多个消费者,一般适用于执行资源密集型任务,单个消费者处理不过来,需要多个消费者进行处理
应用场景:一个订单的处理需要10s,有多个订单可以同时放到消息队列,然后让多个消费者同时处理,这样就是并行了,而不是单个消费者的串行情况

发布订阅模式


一次向许多消费者发送消息,一个生产者发送的消息会被多个消费者获取,也就是将消息将广播到所有的消费者中。
应用场景:更新商品库存后需要通知多个缓存和多个数据库,这里的结构应该是:

一个fanout类型交换机扇出两个个消息队列,分别为缓存消息队列、数据库消息队列
一个缓存消息队列对应着多个缓存消费者
一个数据库消息队列对应着多个数据库消费者

路由模式

有选择地(Routing key)接收消息,发送消息到交换机并且要指定路由key ,消费者将队列绑定到交换机时需要指定路由key,仅消费指定路由key的消息
应用场景:如在商品库存中增加了1台iphone12,iphone12促销活动消费者指定routing key为iphone12,只有此促销活动会接收到消息,其它促销活动不关心也不会消费此routing key的消息

主题模式

根据主题(Topics)来接收消息,将路由key和某模式进行匹配,此时队列需要绑定在一个模式上,“#” 匹配一个词或多个词,“*“ 只匹配一个词。
应用场景:同上,iphone促销活动可以接收主题为iphone的消息,如iphone12、iphone13等

RPC远程调用

如果我们需要在远程计算机上运行功能并等待结果就可以使用RPC,具体流程可以看图。
应用场景:需要等待接口返回数据,如订单支付

发布者确认

与发布者进行可靠的发布确认,发布者确认是RabbitMQ扩展,可以实现可靠的发布。在通道上启用发布者确认后,RabbitMQ将异步确认发送者发布的消息,这意味着它们已在服务器端处理
应用场景:对于消息可靠性要求较高,比如钱包扣款。

posted @ 2021-08-05 16:12  Leejk  阅读(166)  评论(0编辑  收藏  举报