Appearance
问:消息队列的核心作用是什么?列举几个典型应用场景。
消息队列的核心作用,总结为三个关键词:解耦、异步、削峰。
- 解耦:比如订单系统和库存系统,如果直接RPC调用,库存服务挂了订单就失败。用消息队列后,订单服务发个消息就走,库存服务自己慢慢消费,两边互不影响。
- 异步:像用户注册后发短信、邮件这些非核心操作,同步等响应太慢。丢到消息队列异步处理,用户感觉不到延迟,主流程响应更快。
- 削峰:大促时秒杀请求瞬间暴涨,用队列把请求缓冲住,后端按处理能力消费,避免服务被压垮。
场景:
- 订单创建之后,发一条预处理消息,对订单进行预处理,包括地址匹配、sku商品匹配、bom拆分等等。
- 订单的审核,以单号分发每一条消息,包括订单状态修改、库存扣减、优惠券核销等等。
- 日志采集,将日志发到消息队列,由后台程序统一处理。
问:Kafka、RabbitMQ、RocketMQ的主要区别是什么?如何根据业务需求选择?
主要区别:
- RabbitMQ 像个"邮政系统"——擅长灵活的路由和可靠投递,但吞吐量有限。
- Kafka 是"高速公路"——追求超高吞吐量,适合数据洪流,但实时性稍弱。
- RocketMQ 更像"高铁"——平衡吞吐和事务,尤其擅长顺序消息和分布式事务。
运维成本差挺多:
- RabbitMQ:运维最简单,Web控制台能看队列堆积/重发消息,但集群扩展麻烦。
- Kafka:扩容得分区重平衡,监控得用Exporter+Prometheus,但横向扩展能力最强。
- RocketMQ:运维工具最全(自带监控台),支持平滑扩容,但NameServer高可用要自己搭。
我们现在技术选型的一般习惯:
- 中小项目用RabbitMQ(开发运维快)
- 大数据量用Kafka(节省机器)
- 钱相关的业务必用RocketMQ(少赔钱就是赚钱)
- OFS选用RabbitMQ,因为OFS面对的是定制化客户,RabbitMQ吞吐量已经足够,并且运维快速。
- OMS选用RocketMQ,因为OMS是SaaS化的,运维工具全面,支持平滑扩容。
问:如何保证消息的顺序性?Kafka和RocketMQ以及RabbitMQ的实现方式有何不同?
举例:订单的地址解析成功之后,才能匹配快递。
- Kafka:分区锁顺序(物理隔离)。生产者用 分区键(如订单ID) 绑定消息到同一分区。消费者单线程消费同一分区(一个分区=一个顺序单元)。缺陷:分区扩容时,相同订单ID可能被路由到新分区,导致 历史消息和新消息顺序错乱。
- RocketMQ:队列锁+消费标记(逻辑锁)。生产者通过 MessageQueueSelector 绑定订单ID到特定队列。消费者开启 ConsumeMode.ORDERLY(顺序消费模式),对队列加锁消费。
- RabbitMQ:无原生支持。靠业务层实现单通道。例如结合状态标识,订单地址解析完之后才发出匹配快递的消息。
问:保证消息的顺序性情况下,如果消费者处理失败,如何避免顺序错乱?
- RocketMQ:可控重试。消费失败MQ在当前队列原地重试,超过重试次数后,消息转入死信队列异步修复。
- Kafka:旁路存储+跳过。当遇到不可修复错误, 将错误消息存入Redis或数据库,记录订单ID+错误原因,提交当前消息的offset(假装消费成功), 后台线程扫描错误消息补偿。
- RabbitMQ:无原生支持。业务处理。例如:消费者监听队列,失败时进行N次本地重试,彻底失败则发到死信队列。
问:死信队列
在RabbitMQ中,消息要进死信队列必须满足 三选一:
- 消息被拒绝(basic.reject或basic.nack)且requeue=false
- 消息超时过期(TTL到点)
- 队列满员(队列长度超限)
问:RabbitMQ如何实现延迟消息?是否使用死信队列(DLX)?
- 创建普通队列,设置消息过期时间 + 死信路由规则
- 绑定死信队列
- 生产者发送延迟消息
- 消息普通队列休眠,过期后进入死信队列被消费者处理
问:RocketMQ的延迟消息支持哪些级别?
RocketMQ用固定延迟队列避免扫描全量消息,牺牲灵活性换吞吐量。 预设了18个延迟等级,只能选固定档位:
| 延迟等级 | 实际延迟时间 | 适用场景 |
|---|---|---|
| Level 1 | 1秒 | 秒级重试(如支付回调) |
| Level 2 | 5秒 | 短时延迟校验 |
| Level 3 | 10秒 | 简单缓冲 |
| Level 4 | 30秒 | 订单状态流转 |
| Level 5 | 1分钟 | 优惠券锁定释放 |
| Level 6 | 2分钟 | 待支付订单提醒 |
| Level 7 | 3分钟 | 异步操作超时 |
| Level 8 | 4分钟 | 临时库存预留 |
| Level 9 | 5分钟 | 订单未支付预释放 |
| Level 10 | 6分钟 | 预约类业务 |
| Level 11 | 7分钟 | 复杂操作延迟触发 |
| Level 12 | 8分钟 | 级联操作间隔 |
| Level 13 | 9分钟 | 多系统协同缓冲 |
| Level 14 | 10分钟 | 拼团订单等待成团 |
| Level 15 | 20分钟 | 物流状态跟踪 |
| Level 16 | 30分钟 | 直播商品上架预告 |
| Level 17 | 1小时 | 订单自动确认收货 |
| Level 18 | 2小时 | 长周期任务(如退款审核) |
问:RabbitMQ的队列阻塞如何排查?消息积压如何快速处理?是否需扩容消费者或丢弃消息?
RabbitMQ自带监控工具,可查看队列堆积情况。大部分原因如下:
- 内存爆仓(最常见!):查看内存状态(关键指标:memory_alarm),看是否内存超限。可以临时扩容内存,提高内存警戒线。
- 磁盘摆烂(暗藏杀机):查看磁盘状态,看是否空间不足,删除堆积队列(核武器!)或扩容磁盘(需运维配合)。
- 消费者作死(最易忽略):管理界面看到消费者在线,但 Unacked(未确认) 消息暴涨,同一连接的其他队列也被阻塞。
问:RabbitMQ架构了解情况
RabbitMQ的四大核心组件包括:
- 生产者(Producer):负责创建并发送消息到RabbitMQ服务器。
- 消费者(Consumer):从队列中获取并处理消息。
- 队列(Queue):存储消息,等待消费者处理。
- 交换机(Exchange):接收生产者发送的消息,并根据路由规则将消息转发到一个或多个队列。
问:RabbitMQ的高可用,镜像队列如何配置?
RabbitMQ 有三种模式:单机模式、普通集群模式、镜像集群模式。
单机模式
- 特点:所有队列、交换机等组件都运行在一台服务器上。
- 适用场景:开发或测试环境,不适合生产环境,因为不具备高可用性。
普通集群模式
- 特点:多台服务器上部署多个 RabbitMQ 实例,队列元数据在集群中同步,但消息实体只存储在一个节点上。
- 优点:提高了系统的可用性和吞吐量。
- 缺点:如果存储消息的节点故障,可能导致消息丢失。
- 适用场景:需要提高可用性,但对数据可靠性要求不高的场景。
镜像集群模式
- 特点:队列和消息在多个节点间镜像同步,确保数据冗余和故障转移。
- 优点:具有高可用性和数据可靠性,节点故障时不会影响消息的可用性。
- 缺点:消息同步会增加网络带宽压力,扩展性受限。
- 适用场景:需要同时保证高可用性和数据可靠性的场景。
在选择 RabbitMQ 的部署模式时,需要根据实际的业务需求、系统架构以及对可用性和数据可靠性的要求来决定。
问:RabbitMQ持久化
RabbitMQ通过以下方式实现持久化:
- 交换机持久化:在声明交换机时,将durable参数设置为true,这样交换机在RabbitMQ重启后仍能保留123。
- 队列持久化: 在声明队列时,将durable参数设置为true,队列及其元数据将在RabbitMQ重启后保留345。
- 消息持久化:发送消息时,将deliveryMode设置为2(持久化模式),消息将被存储到磁盘中,确保在RabbitMQ重启后不丢失367。
这样即使服务器重启了,它也能从硬盘上把这些“记忆”读取回来,恢复到宕机前的状态。
问:如何保证消息不丢失(可靠性)?从生产者、Broker、消费者三方面说明。
- 生产者:开启确认模式(Confirm Mode):生产者通过调用 channel.confirmSelect() 方法开启确认模式。在这种模式下,当生产者发送消息后,RabbitMQ会向生产者发送一个确认消息,告知生产者该消息是否成功到达服务器。
- 消费者: ACK确认机制 + 死信队列(DLX)。
- ACK确认机制:消费者处理完消息后显式发送ACK,避免消息因崩溃丢失。
- 死信队列(DLX):处理因消费者崩溃或消息超时未被确认的消息。
- Broker防御(防仓库丢件):加入消息持久化。
问:如何处理消息重复消费?是否依赖业务幂等性?
RabbitMQ 本身无法完全避免消息重复消费,其机制必然依赖业务层幂等性设计。
消息重复的核心根源:
- 生产者重复发送:网络闪断导致Confirm超时,生产者重试
- Broker重复投递:消费者ACK失败后消息重回队列(requeue=true)
- 消费者重复处理:业务处理成功但ACK丢失(如进程崩溃)
一般业务实现方式:
- 加入版本号控制
- 业务状态检查