RabbitMQ/AMQP中的一些概念以及与Redis/Kafka/RocketMQ的区别

消息投递:Exchange & RoutingKey

在投递消息时需要指定exchange和routingKey,而不是指定队列名。

在投递消息时和kafka、rocketmq不同,消息并不是通过客户端投递到Queue中的,而是投递到Exchange,然后Exchange根据自身的规则以及传进来的routingKey决定将消息分发到那个或哪些Queue。

exchange只是转发规则,并不会负责存储任何消息。

exchange有多种类型,不同的类型决定了如何使用routingKey(比如全匹配、通配符匹配)

消费消息:Queue

Queue是负责存储消息的,和其他队列一样消费者在消费消息时也是直接从Queue中消费。

结合上面对消息投递的说明,会发现投递和消费并不是同一个东西,投递时用的时Exchange,消费时用的是Queue,这就是RabbitMQ和其它队列的一个重大区别。

Queue中的消息如果被消费了(并且Ack了),将彻底从RabbitMQ中删除。这一点和Redis比较像,与Kafka/RocketMQ完全不同。

因此不能重新消费历史数据,也不可以让两个不同的业务消费同一个Queue

路由规则:Binding

Binding负责将Exchange和Queue串联起来(也可以将Exchange和另一个Exchange串联起来,这个是RabbitMQ的实现,不是AMQP协议的规范)

设置Binding的时候要指定一个Exchange和Queue,同时指定routingKey是什么,以direct类型的Exchange为例,当exchange1和queue1中设置了一个abc的routingKey,那么当客户端往exchange1投递消息,并且带上abc这个routingKey,消息就会进入queue1

如何两个业务消费相同的消息?

在使用消息队列时经常会用到订阅主题的模式(观察者模式),他可以大大降低代码的耦合度,比如在注册账号时用户服务本身只是需要把user表创建好就可以了。但是往往在注册环节会有大量的额外工作,比如发欢迎邮件、送新人礼包、给大数据中心提供信息等等,这些业务虽然和注册行为有关,但都放到注册的业务流程里会让代码耦合度很高,不宜扩展,比如送新人礼包这件事儿就随时有可能调整或下线,但他又不会影响注册这件事儿。这时我们可以让用户服务在创建完用户后发送一条又新用户注册的消息出来,用户服务自己不去管后面还有哪些逻辑,而是由需要观察注册这个事件的业务方去订阅这个消息并处理自己的业务。

在Kafka和RocketMQ中可以通过使用不同的Group分别记录各自的offset实现同一条消息被不同的业务消费,但是RabbitMQ在消费成功后会立即从Queue中删掉这条消息,那么如何在RabbitMQ中对同一条消息消费两次呢。

可以在一个exchange上设置两个相同的routingKey但不同Queue的binding,这时消息在经过exchange1后会被分别投递到queue1和queue2

需要注意一点,当创建queue2的那条binding时,并不会将之前的已经路由过的abc消息复制过来,只会将创建binding后的产生新消息复制过来。

一个队列也可以订阅多种消息

因为queue和exchange之间是多对多的关系,因此也可以让一个queue和多个exchange建立关联关系,甚至一个queue和同一个exchange建立多条关联关系。

特殊Exchange(default)

RabbitMQ中有一个默认的自带的Exchange,无法被删除,在后台看名字叫(default),后台的名字中就带括号。且无法给这个exchange增加binding,但是它有一个隐含的规则,会将消息按生产者客户端传入的routingKey,路由到同名的queue中。

客户端如果想使用这个Exchange,在发送消息时Exchange应当设置为空或空字符串(不同的语言可能略有差异)。

无法路由

如果生产者发消息时传入了一个不存在的exchange,或者这个exchange中没有能够应用在以这一条消息上的Bindding,那么这条消息将直接丢弃。

发表评论

电子邮件地址不会被公开。 必填项已用*标注