Skip to content

RabbitMQ

介绍消息队列的作用?

  • 吞吐量提升:无需等待订阅者处理完成,响应更快速
  • 故障隔离:服务没有直接调用,不存在级联失败问题
  • 调用间没有阻塞,不会造成无效的资源占用
  • 耦合度极低,每个服务都可以灵活插拔,可替换
  • 流量削峰:不管发布事件的流量波动多大,都由 Broker 接收,订阅者可以按照自己的速度去处理事件
bash
消息队列主要有三大使用场景,分别是异步,流量萧峰和应用解藕。另外还包含日志 和消息通讯在项目中为了实现异步调用,所以采用消息队列进行服务调用

常见的 mq 产品?

追求可用性:Kafka、 RocketMQ 、RabbitMQ

追求可靠性:RabbitMQ、RocketMQ

追求吞吐能力:RocketMQ、Kafka(大吞吐量才会去使用)

追求消息低延迟:RabbitMQ、Kafka

1.RabbitMQ 工作模式/消息模式(5 个 发布队列 3 个)

  • 简单模式:生产者发送消息到队列,使用默认的交换机,消费者监听队列
  • 工作模式:多个消费者监听一个队列,默认消息平均消费,但可以设置能者多劳模式
  • 发布订阅-广播:交换机接收到消息后,会路由到所有与他绑定的队列
  • 发布订阅-路由:会匹配相对的路由,发送给满足条件的队列
  • 发布订阅-主题:路由支持通配符# 0 或多个单词 * 代表一个单词

2.MQ 如何保证顺序消费

RabbitMQ 的 queue 本身就是队列,是可以保证消息的顺序投递的。

但是消息的顺序消费则是另一回事了,所谓的“顺序消费”意味着是否顺序达到目的地,比如:数据库。

看看以下场景:

一个 queue,多个 consumer。比如,生产者向 RabbitMQ 里发送了三条数据,顺序依次是 data1/data2/data3,压入的是 RabbitMQ 的一个内存队列。有三个消费者分别从 MQ 中消费这三条数据中的一条,结果消费者 2 先执行完操作,把 data2 存入数据库,然后是 data1/data3。这不明显乱了。

1650554619432-3c20920c-be04-4dc3-8a3b-4d7370b284cd.png

产生多个 consumer 去消费一个 queue,极有可能是因为:消息消费太慢,所以盲目让多个 consumer 同时来消费,而忽略了消息消费顺序性。

在某些情况下,消息是需要保证顺序性的,如果上图中的 data1, data2, data3 分别意味着对某条数据的增改删,但是如果乱序以后就变成了:删改增。

解决方案

  1. 拆分多个 queue,每个 queue 一个 consumer。
  2. 一个 queue,但是对应一个 consumer,然后这个 consumer 内部用内存队列(其实就是 List 而已)做排队,然后分发给底层不同的 thread 来处理(此方案可以支持高并发)。
  3. 实际 consumer 的数量是受限的,不会仅仅因为消息消费太慢而去增加 consumer 实例的数量,所以通过方案 2 的方式,可以在不增加 consumer 实例数量的前提下,加快消息消费的速度。

1650554711239-eb78ae09-72d4-4e40-868b-e3962491c6d2.png

3、MQ 如何消息不丢失,持久化

有四种方式可以解决:

  1. 消息持久化
  2. ACK 确认机制
  3. 设置集群镜像模式
  4. 消息补偿机制

消息持久化解决

  1. Exchange 设置持久化
  2. Queue 设置持久化
  3. Message 持久化发送:发送消息设置发送模式 deliveryMode=2,代表持久化消息
bash
1.队列,交换机持久化,保证数据不丢失
2.定义信息重试机制
3.手动ack确认

rabbitmq 如何防止消息重复消费?

每个消息用一个唯一标识来区分,消费前先判断标识有没有被消费过,若已消费过,则直接ACK
bash
1.事物机制 生产者发送前开启事物机制,没有收到消息会回滚
2.保持幂等性,可利用redis来构建一个唯一id实现幂等性

4、MQ 的消息确认机制

  1. 为了保证消息从队列可靠的达到消费者,RabbitMQ 提供了消息确认机制(Message Acknowledgement)。消费者在订阅队列时,可以指定 autoAck 参数,当 autoAck 参数等于 false 时,RabbitMQ 会等待消费者显式地回复确认信号后才从内存(或者磁盘)中移除消息(实际上是先打上删除标记,之后在删除)。当 autoAck 参数等于 true 时,RabbitMQ 会自动把发送出去的消息置为确认,然后从内存(或者磁盘)中删除,而不管消费者是否真正地消费到了这些消息。
  2. 采用消息确认机制后,只要设置 autoAck 参数为 false,消费者就有足够的时间处理消息(任务),不用担心处理消息过程中消费者进程挂掉后消息丢失的问题,因为 RabbitMQ 会一直等待持有消息直到消费者显式调用 Basic.Ack 命令为止。
  3. 当 autoAck 参数为 false 时,对于 RabbitMQ 服务器端而言,队列中的消息分成了两部分:一部分是等待投递给消费者的消息;一部分是已经投递给消费者,但是还没有收到消费者确认信号的消息。如果 RabbitMQ 服务器端一直没有收到消费者的确认信号,并且消费此消息的消费者已经断开连接,则服务器端会安排该消息重新进入队列,等待投递给下一个消费者(也可能还是原来的那个消费者)。
  4. RabbitMQ 不会为未确认的消息设置过期时间,它判断此消息是否需要重新投递给消费者的唯一依据是消费该消息连接是否已经断开,这个设置的原因是 RabbitMQ 允许消费者消费一条消息的时间可以很久很久。

5、MQ 高可用

镜像集群模式

1650557061244-ed4c5750-ba29-4fb2-9b7f-1703c03a9716.png

  1. 无论元数据还是 queue 里的消息都会存在于多个 broker 上
  2. 每个 queue 都想拥有多个镜像放在其他 broker 上,可以选择镜像队列的数量
  3. 由于每个 broker 上都具有近乎完整的数据,所以消费者消费的时候并不需要进行消息传输,但由于并不是想 Kafka 分布式消息队列那样的分片存储,所以性能并不高

小小总结一下,RabbitMQ 其实并不是分布式消息队列,大厂使用的分布式消息队列,更多是 RocketMQ 或者 Kafka,可以分布式分片存储,水平扩容性能会有明显提升

6、消息生产方将消息成功投递到消息消费方,消息消费成功了, 业务逻辑执行失败了, 问怎么办?

自动确认机制 改成 手动确认,在业务逻辑 try...catch{} 拒绝接收,消息重放到 MQ 队列

消息转换器的作用?

MessageConverter的作用主要有两方面.

- 一方面它可以把我们的非标准化Message对象转换成我们的目标Message对象,这主要是用在发送消息的时候;
- 另一方面它又可以把我们的Message对象转换成对应的目标对象,这主要是用在接收消息的时候。

rabbitmq 如何保证消息的可靠性?

(1) 开启 confirm
(2) 开启 RabbitMQ 的持久化 (交换机、队列、消息)
(3) 关闭 RabbitMQ 的自动 ack(改成手动)

(4) 配置消费重试次数,消费重试间隔时间等

rabbitmq 如何避免消息堆积?

1.去优化消费者代码,提高消费能力。减少消费时间
2.可以给消费设置年龄(生命周期),如果超时就丢弃掉。可以不让消息大量堆积在消息队列中
3.可以设置队列的最大长度:如果超过了,就无法接收消息到队列中。
4.建立新的消息队列,采用订阅模式,消费者同时去订阅新的,还有旧的消息队列,同时去消费消息。

最近更新