什么是 MQ
MQ:Message Queue,消息队列。消息由生产者发送到 MQ 进行排队,然后由消费者对消息进行处理。QQ、微信就是典型的 MQ 场景
MQ 的作用或者是优点:
- 异步:
- 例子:快递, 快递员 -> 菜鸟驿站 <- 客户
- 作用:异步提高了系统的响应速度和吞吐量
- 解耦:
- 例子:英文文献 -> 翻译机构 <- 阅读者可以读到中文文献
- 作用:服务之间进行解耦,可以较少服务之间的影响,提高系统的稳定性和可扩展性;另外,解耦后可实现数据分发,生产者发送一个消息,可以被多个消费者消费。
- 削峰:
- 长江涨水 -> 三峡大坝;秒杀系统
- 作用:以稳定的系统资源应对突发的流量冲击,减少高峰时期对服务器压力。
MQ缺点:
- 系统可用性降低:系统引入的外部依赖越多,越容易挂掉。万一 MQ 挂了,MQ 一挂,整套系统崩溃
- 系统复杂度提高
- 一致性问题:A 系统处理完了直接返回成功了,人都以为你这个请求就成功了;但是问题是,要是 BCD 三个系统那里,BD 两个系统写库成功了,结果 C 系统写库失败了
- 消息重复消费
- 消息顺序消费
- 消息积压
如何保证消息的可靠传输?如果消息丢了怎么办
数据的丢失问题,可能出现在生产者、MQ、消费者中

- 生产者丢失:生产者将数据发送到 RabbitMQ 的时候,可能数据就在半路给搞丢了,因为网络问题啥的,都有可能。此时可以选择用 RabbitMQ 提供的事务功能,就是生产者发送数据之前开启 RabbitMQ 事务channel.txSelect,然后发送消息,如果消息没有成功被 RabbitMQ 接收到,那么生产者会收到异常报错,此时就可以回滚事务channel.txRollback,然后重试发送消息;如果收到了消息,那么可以提交事务channel.txCommit。吞吐量会下来,因为太耗性能。所以一般来说,如果你要确保说写 RabbitMQ 的消息别丢,可以开启confirm模式,在生产者那里设置开启confirm模式之后,你每次写的消息都会分配一个唯一的 id,然后如果写入了 RabbitMQ 中,RabbitMQ 会给你回传一个ack消息,告诉你说这个消息 ok 了。如果 RabbitMQ 没能处理这个消息,会回调你一个 nack接口,告诉你这个消息接收失败,你可以重试。而且你可以结合这个机制自己在内存里维护每个消息 id 的状态,如果超过一定时间还没接收到这个消息的回调,那么你可以重发。事务机制和 cnofirm机制最大的不同在于,事务机制是同步的,你提交一个事务之后会阻塞在那儿,但是 confirm机制是异步的,你发送个消息之后就可以发送下一个消息,然后那个消息RabbitMQ 接收了之后会异步回调你一个接口通知你这个消息接收到了。所以一般在生产者这块避免数据丢失,都是用confirm机制的。
- MQ中丢失:就是 RabbitMQ 自己弄丢了数据,这个你必须开启 RabbitMQ 的持久化,就是消息写入之后会持久化到磁盘,哪怕是 RabbitMQ 自己挂了,恢复之后会自动读取之前存储的数据,一般数据不会丢。设置持久化有两个步骤:创建 queue 的时候将其设置为持久化,这样就可以保证 RabbitMQ 持久化 queue 的元数据,但是不会持久化 queue 里的数据。第二个是发送消息的时候将消息的 deliveryMode 设置为2,就是将消息设置为持久化的,此时 RabbitMQ 就会将消息持久化到磁盘上去。必须要同时设置这两个持久化才行,RabbitMQ 哪怕是挂了,再次重启,也会从磁盘上重启恢复 queue,恢复这个 queue 里的数据。持久化可以跟生产者那边的confirm机制配合起来,只有消息被持久化到磁盘之后,才会通知生产者ack了,所以哪怕是在持久化到磁盘之前, RabbitMQ 挂了,数据丢了,生产者收不到ack,你也是可以自己重发的。注意,哪怕是你给 RabbitMQ 开启了持久化机制,也有一种可能,就是这个消息写到了 RabbitMQ 中,但是还没来得及持久化到磁盘上,结果不巧,此时 RabbitMQ 挂了,就会导致内存里的一点点数据丢失。
- 消费端丢失:你消费的时候,刚消费到,还没处理,结果进程挂了,比如重启了,那么就尴尬了,RabbitMQ 认为你都消费了,这数据就丢了。这个时候得用 RabbitMQ 提供的ack机制,简单来说,就是你关闭 RabbitMQ 的自动ack,可以通过一个 api 来调用就行,然后每次你自己代码里确保处理完的时候,再在程序里ack一把。这样的话,如果你还没处理完,不就没有ack?那 RabbitMQ 就认为你还没处理完,这个时候 RabbitMQ 会把这个消费分配给别的 consumer 去处理,消息是不会丢的。
如何保证消息的顺序性
一个 Queue 对应一个消费者,或者消费者排队
如何保证消息消费的幂等性?
幂等性:防止消费者重复消费问题
所有 MQ 产品并没有提供主动解决幂等性的机制,需要由消费者自行控制。
RocketMQ:给每个消息分配一个 MessageID,这个 MessageID 就可以作为消费者判断幂等性的依据(不太建议,最好是自己带一个有业务标识的 ID(如 OrderID),来进行幂等性判断,参考 ID 生成)