MQ简历话术
本专栏只总结最重要的八股,简历对应的
简历话术:
RabbitMQ:理解异步、削峰、解耦;熟悉消息可靠性、幂等性、顺序性、死信队列和延时队列及消息积压问题的解决方案
1. 作用:异步削峰解耦
电商秒杀系统(综合体现异步+削峰+解耦) 场景描述:双11零点,某商品限量秒杀,瞬间涌入百万级并发请求。
异步(秒杀用户立即得到“排队中”响应):用户点击“立即秒杀”后,立即返回“排队中,请稍后查看结果”。后端消费者慢慢拉取MQ消息,真正完成订单创建、扣库存、发券等操作。
削峰(流量大时不让服务垮掉):网关层只校验资格、生成秒杀令牌,不处理后续逻辑。所有有效请求直接写入MQ(例如订单预处理队列),瞬间峰值被“削”成平缓的队列堆积。
解耦(新增下游零代码改动):秒杀核心服务只负责“资格校验+写MQ”。至于后续是扣库存、生成订单、发积分还是推送消息,都由独立的消费者监听不同队列完成。任何一个下游故障,不会影响秒杀主流程。 支付回调接收服务只做一件事——收到支付成功消息后,立即写入MQ。之后订单状态更新、发货、加积分等,各自监听MQ独立消费。哪怕加积分服务挂了,其他服务完全不受影响。
扩展
MQ 的优缺点
优点:异步,削峰,解耦
缺点
引入新的中间件必然系统可用性降低(如果 MQ 服务宕机,所有依赖 MQ 的服务都会崩溃),系统复杂度提高,必须额外处理:可靠性、幂等性、延时队列、顺序性及消息积压问题(简历后面的),数据一致性问题 A 系统处理完并返回成功(用户认为请求已完成),但 B、C、D 中可能 C 写库失败,导致数据不一致。
2. 如何保证消息可靠性(防止消息丢失)
2.1 消息丢失的三种可能环节
- 发送时丢失(生产者 → 交换机(Confirm 机制) 交换机 → 队列(Return 机制))
- MQ 宕机丢失(队列已接收但未持久化)
- 消费时丢失(消费者拿到消息后未处理完就宕机)
2.2 生产者端的可靠性保障
2.2.1 Confirm 机制(发送确认)
- 开启 confirm 模式后,每条消息分配唯一 ID。
- 成功投递到交换机 → 返回
ack - 未投递到交换机 → 返回
nack,生产者可重试或记录日志。
3.2.2 Return 机制(路由回执)
- 消息成功投递到交换机,但未能路由到队列 → 返回
ack及路由失败原因。 - 消息成功路由到队列 → 不返回任何信息。
- 项目中配置
ConfirmCallback和ReturnCallback分别处理未确认和未路由的情况。
2.3 MQ 端的可靠性保障
- 消息持久化:将队列和消息均持久化到磁盘。即使 RabbitMQ 宕机重启,也能自动恢复数据,基本不会丢失。
2.4 消费者端的可靠性保障
- 手动 ACK:消费者处理完业务逻辑后,再手动向 MQ 发送
ack回执。MQ 收到ack后才删除消息;若消费者宕机,消息会重新投递。
3. 消息幂等性(解决重复消费)
3.1 重复消费的产生原因(根本原因在于网络不可靠)
- 网络原因导致生产者未收到
ack,生产者重发消息。 - 网络原因导致消费者未成功发送
ack,MQ 重复投递消息。
消息重复消费是无法完全避免的,因此消费端必须实现幂等性。
3.2 幂等性解决方案
3.2.1 唯一约束(数据库)
- 消费数据准备入库时,利用数据库表的唯一键约束,重复插入会报错,不会产生脏数据。
- 或先查询数据库是否已存在,存在则丢弃(高并发下可能存在写入瓶颈)。
3.2.2 消息唯一 ID + Redis
- 生产者发送消息时附带一个全局唯一 ID。
- 消费者先将该 ID 存入 Redis(并设置过期时间),每次消费前查询 Redis 是否已存在,若存在则直接丢弃。
4. 消息顺序性
4.1 为什么要保证顺序
若多个消息对同一数据进行操作且具有前后依赖关系(例如依次执行 插入 → 更新 → 删除),必须按顺序执行,否则会造成数据错误。
4.2 顺序错乱的典型场景
一个队列被多个消费者并发消费。虽然消息从 MQ 读取时是有序的,但每个消费者执行速度不同,可能导致先读取的消息后完成,从而打乱顺序。
4.3 RabbitMQ 的解决方案
- 一个队列对应一个消费者:将需要保证顺序的消息发送到同一个队列,且该队列只由一个消费者处理。缺点:队列变多,吞吐量下降。或在消费者内部使用内存队列 + 多线程,按顺序处理(同一条数据(同一个订单)的消息一定进入同一个内存队列,由同一个线程顺序处理 → 顺序性得到保证。不同数据可以并行处理)。
注:Kafka 通过分区(partition)内有序、RocketMQ 通过顺序消息机制提供更好的原生支持;RabbitMQ 需要自行设计上述方案。
5. 死信队列与延时队列
5.1 死信队列(DLX)
5.1.1 什么是死信
死信即无法被正常消费的消息。当一个队列中的消息满足以下任一条件时,会变成死信:
- 消息被消费者拒绝(人不要)
- 消息 TTL 到期,超时无人消费(过期了)
- 队列已满,最早的消息成为死信(没人要放不下)
5.1.2 死信的处理
- 若队列配置了
dead-letter-exchange属性,死信会被投递到指定的死信交换机(DLX),进而进入死信队列。(DLX是自己声明的一个交换机为DLX,再把一个普通队列绑定DLX,即为死信队列) - 用途:作为消息消费失败的兜底方案,可将异常消息持久化到 MySQL/Redis,供人工处理;也可处理消息超时或队列满的问题。
5.2 延时队列
5.2.1 什么是延时队列
消息发送到 MQ 后不立即被消费,而是等待指定时间后才允许消费者获取。
5.2.2 典型场景
订单超时取消:订单信息放入 MQ,30 分钟后消费者才拿到该消息,直接取消订单,避免轮询数据库。(让消费者拿到消息后,先检查订单状态,如果已经支付,就直接忽略(ack 掉),不执行取消操作)
5.2.3 RabbitMQ 实现延时队列的方式
RabbitMQ 没有内置延时队列,需要通过 TTL + 死信交换机 模拟:
- 创建队列 A,设置消息的 TTL(存活时间)。
- 队列 A 中的消息在 TTL 到期后成为死信,通过 DLX 转发到队列 B。
- 消费者监听队列 B,获取到的消息即为延迟后的消息。
6. 消息积压的处理
6.1 消息积压的原因
- 生产者发送消息的速度 > 消费者处理速度。
- 消费者因 bug 或阻塞持续无法消费。
- 结果:队列消息堆积,最早的消息可能成为死信被丢弃。
6.2 常规优化方法
- 增加更多消费者,提高消费速度。
- 在单个消费者内部使用线程池,加快消息处理(注意 CPU 压力)。
6.3 极端情况处理(百万级消息积压 消费者bug导致)
- 先修复消费者代码的 bug,保证修复后消费能力正常。 把现有的所有消费者全部停掉,避免继续无效消费、产生更多错误。
- 分发到十倍临时队列: 编写一个临时分发程序,将积压消息均匀轮询写入这 10 倍的队列中。
- 消费: 临时征用 10 倍的机器部署消费者,每个消费一个临时队列。
- 恢复原来: 积压消完后,销毁临时队列、临时消费者,恢复原来正常的消费架构,继续正常消费
针对面试,整理简历上写的每行技术点+对应完整话术和扩展点,快速速成
查看4道真题和解析