今日主题:MQ核心原理、三大主流MQ对比、消息可靠性(不丢失/不重复/顺序性)、事务消息、死信队列、落地配置及面试高频考点(全程贴合实战,可直接写简历、面试口述)
核心目标:吃透MQ的「核心作用+落地细节+问题解决方案+面试必背」,重点突破消息可靠性、事务消息等难点,掌握RocketMQ/Kafka核心配置,能独立搭建MQ集群、排查线上问题
一、MQ核心基础
1. 什么是MQ(消息队列)
定义:一种跨服务的异步通信组件,本质是“消息中转站”,接收生产者发送的消息,缓存消息,再异步推送给消费者,实现“生产者和消费者解耦、异步通信、削峰填谷”。
核心价值:不依赖服务间直接调用,即使消费者宕机,消息也能缓存,避免服务雪崩,提升系统高可用和并发能力。
2. 为什么要用MQ
-
异步通信(核心作用1)
-
适用场景:非核心流程异步化,主线程快速返回,提升用户体验
-
实例:用户下单流程(核心流程:创建订单→扣减库存;非核心流程:发送短信通知→生成物流单→积分增加)
- 无MQ:下单接口需要同步调用4个服务,响应时间=各服务耗时之和(如500ms),用户等待久
- 有MQ:下单接口只执行核心流程(200ms),非核心流程丢到MQ异步执行,用户快速收到下单成功反馈
-
落地要点:非核心流程可异步,核心流程(如下单、支付)需保证消息可靠性
-
-
解耦(核心作用2)
- 适用场景:服务间关联性低,避免一个服务变更影响其他服务
- 实例:订单服务和物流服务,无MQ时,订单服务直接调用物流服务接口,物流服务宕机,订单服务会报错;有MQ时,订单服务发送消息到MQ,无论物流服务是否正常,订单服务都能正常执行,物流服务恢复后消费消息即可。
- 落地要点:通过MQ解耦后,服务间只需约定消息格式,无需关心对方的部署、版本变更
-
削峰填谷(核心作用3)
- 适用场景:高并发场景(如秒杀、大促),瞬间流量过大,避免打崩数据库/下游服务
- 实例:秒杀场景,瞬间QPS从1k飙升到10万,数据库只能承受1k QPS,通过MQ缓存所有秒杀请求,消费者按数据库处理能力匀速消费(如1k QPS),避免数据库宕机。
- 落地要点:MQ需开启持久化,避免消息丢失;消费者需合理设置并发数,匹配下游服务处理能力
-
最终一致性(核心作用4)
- 适用场景:分布式事务场景,实现跨服务数据一致性(如订单创建和库存扣减)
- 实例:通过MQ事务消息,保证“订单创建成功”和“库存扣减成功”的最终一致性,避免订单创建成功但库存未扣减,或库存扣减成功但订单未创建。
3. MQ核心架构(通用模型,所有MQ都适用)
-
生产者(Producer) :发送消息的服务/应用(如下单服务、用户服务),负责将消息发送到MQ的队列/主题中。
-
消费者(Consumer) :接收并处理消息的服务/应用(如物流服务、短信服务),从MQ中拉取/接收消息,执行业务逻辑。
-
队列/主题(Queue/Topic) :消息的存储容器,核心区别:
- 队列(Queue):点对点模式,一条消息只能被一个消费者消费(如订单通知消息,只需要一个消费者处理)
- 主题(Topic):发布/订阅模式,一条消息可以被多个消费者消费(如商品降价消息,多个服务需要接收)
-
Broker:MQ的核心服务节点,负责接收、存储、转发消息(如RocketMQ的NameServer+Broker,Kafka的Broker),集群部署保证高可用。
-
消息(Message) :通信的载体,包含消息ID(唯一标识)、消息体(业务数据,如订单信息)、消息属性(如过期时间、标签)。
4. MQ的两种消费模式(落地必选)
-
拉取模式(Pull) :消费者主动从MQ拉取消息,灵活控制消费节奏,但会产生轮询开销(如Kafka默认拉取模式)。
- 落地细节:设置合理的拉取间隔和拉取数量,避免频繁拉取(空轮询),也避免拉取过多消息导致消费者过载。
-
推送模式(Push) :MQ主动将消息推送给消费者,实时性高,但可能导致消费者被消息压垮(如RocketMQ默认推送模式)。
- 落地细节:设置消费者线程池大小,开启流控,避免消息堆积时MQ持续推送,压垮消费者。
二、三大主流MQ对比
核心选型原则:优先选国内互联网主流、文档完善、落地案例多的MQ,避免小众MQ(维护成本高),重点对比RocketMQ、Kafka、RabbitMQ,三者占市场90%以上份额。
| 对比维度 | RocketMQ(阿里开源,国内主流) | Kafka(Apache,大数据场景首选) | RabbitMQ(Erlang开发,轻量易用) |
|---|---|---|---|
| 吞吐量 | 高(10万+ QPS),支持高并发业务 | 极高(100万+ QPS),大数据场景最优 | 中等(万级 QPS),适合中小系统 |
| 可靠性 | 高(支持持久化、事务消息、重试、死信),适合业务场景 | 中高(持久化可靠,但事务消息支持弱),适合日志/流处理 | 高(支持持久化、重试),但集群扩展能力弱 |
| 核心优势 | 事务消息、延迟消息、死信队列完善,中文文档,适配国内业务,阿里生态支持 | 吞吐量极高,日志收集、流处理(Flink/Spark)场景最优,集群扩展能力强 | 轻量、易用、配置简单,支持多种交换机模式,适合小项目、快速落地 |
| 缺点 | 大数据场景吞吐量不如Kafka,跨语言支持一般 | 业务消息功能(如延迟、事务)不完善,配置复杂,运维成本高 | 吞吐量低,Erlang语言开发,二次开发难度大,高并发场景不适用 |
| 适用场景 | 电商、互联网业务(下单、支付、通知),需要事务/延迟消息 | 日志收集、大数据流处理、监控数据上报,高吞吐场景 | 中小系统、后台管理系统,并发量不高,快速落地 |
| 落地成本 | 中(集群部署简单,中文文档,问题易排查) | 高(配置复杂,运维要求高,需配合大数据组件) | 低(一键部署,上手快,运维简单) |
面试必背选型结论:业务场景(电商、支付)优先选RocketMQ;日志、大数据、流处理优先选Kafka;小项目、并发量低、快速落地选RabbitMQ。
三、MQ核心问题
MQ最核心的4个问题:消息丢失、消息重复消费、消息顺序性、消息堆积,这4个问题是面试必问,也是线上高频故障,必须吃透“原因+解决方案+落地细节”。
1. 消息丢失(最严重,线上必防)
核心原因:消息从“生产者→MQ→消费者”三个环节,任一环节出问题都会导致丢失,需逐个环节防护。
(1)三个环节的丢失原因
- 环节1:生产者→MQ:消息未成功发送到MQ(网络波动、MQ宕机、生产者未重试)
- 环节2:MQ自身:消息未持久化,MQ宕机后消息丢失;MQ集群故障,消息未同步到从节点
- 环节3:MQ→消费者:消息已推送,但消费者未处理完成就崩溃;消费者未确认消息(ACK),MQ以为未消费,重新推送但原消息已丢失
(2)全链路解决方案(RocketMQ落地细节,直接用)
-
生产者端防护(避免消息发送失败)
-
开启生产者确认机制(ACK):
- RocketMQ配置:设置producer.setRetryTimesWhenSendFailed(3)(发送失败重试3次),setRetryTimesWhenSendAsyncFailed(3)(异步发送失败重试3次)
- 确认机制:发送消息后,接收MQ的ACK响应,若未收到ACK或收到失败响应,自动重试,重试间隔递增(如100ms、200ms、500ms)
-
使用事务消息(核心业务场景):
- 核心业务(如下单)必须用事务消息,保证“本地事务执行成功”和“消息发送成功”的一致性,避免本地事务成功但消息未发送。
-
日志记录:发送消息时,记录消息ID、业务数据、发送时间,若发送失败,可通过日志排查,手动重试。
-
-
MQ端防护(避免消息存储丢失)
-
开启消息持久化:
- RocketMQ:默认持久化(消息存储到磁盘),配置flushDiskType=SYNC_FLUSH(同步刷盘),确保消息写入磁盘后才返回ACK,避免内存断电丢失。
- Kafka:开启topic持久化(retention.ms设置为7天),消息存储到分区日志文件,避免MQ宕机丢失。
-
MQ集群部署(高可用):
- RocketMQ:采用“NameServer+多Master多Slave”架构,Master节点负责写消息,Slave节点同步消息,Master宕机后,Slave自动切换为Master,避免单点故障。
- 配置细节:Master和Slave同步方式设置为SYNC_MASTER(同步同步),确保消息同步到Slave后,才返回ACK给生产者。
-
-
消费者端防护(避免消息处理丢失)
-
开启手动ACK(核心):
- 禁止使用自动ACK,设置consumer.setMessageModel(MessageModel.CLUSTERING)(集群模式),手动ACK配置:consumer.registerMessageListener((messageListener) -> { // 1. 处理业务逻辑 // 2. 处理成功后,手动ACK return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; // 3. 处理失败,返回RECONSUME_LATER,MQ会重试 });
- 核心逻辑:只有业务逻辑执行完成(无异常),才手动ACK;若执行失败,返回重试状态,MQ会重新推送消息。
-
避免消费者崩溃导致消息丢失:
- 消费者业务逻辑需做幂等性处理(即使消息重复推送,也不影响结果)。
- 消费者集群部署,避免单点故障,一个消费者宕机,其他消费者继续消费。
-
设置消息重试次数:
- RocketMQ配置:consumer.setMaxReconsumeTimes(5)(最多重试5次),重试次数耗尽后,消息进入死信队列,避免无限重试。
-
(3)避坑点(线上高频问题)
- 不要关闭生产者重试:重试是避免网络波动导致消息丢失的关键,即使是核心业务,也需开启重试(但要做幂等性,避免重复消费)。
- RocketMQ刷盘模式:核心业务用同步刷盘(SYNC_FLUSH),非核心业务可用异步刷盘(ASYNC_FLUSH),平衡性能和可靠性。
- 避免消费者手动ACK前崩溃:业务逻辑执行完成后,再ACK,不要提前ACK(如刚接收消息就ACK,还没处理就崩溃,消息丢失)。
2. 消息重复消费(必然发生,必须解决)
核心结论:消息重复消费是无法避免的(网络重试、消费者崩溃、ACK失败、MQ集群切换),解决方案是保证消费幂等性(重复消费=只执行一次)。
(1)重复消费的常见原因
- 生产者重试:消息发送失败,生产者自动重试,导致MQ接收多条相同消息。
- 消费者重试:消费者处理消息失败,MQ重新推送,导致重复消费。
- ACK丢失:消费者处理完成后,手动ACK的响应丢失,MQ以为未消费,重新推送。
- MQ集群切换:Master宕机,Slave切换为Master,未同步的消息可能被重复推送。
(2)幂等性解决方案(落地可直接用,分场景)
-
方案1:基于消息ID去重(通用方案)
-
原理:MQ的每条消息都有唯一ID(如RocketMQ的msgId,Kafka的offset+partition),消费前先判断该消息ID是否已消费,已消费则直接返回成功,未消费则执行业务。
-
落地细节:
-
存储介质:Redis(推荐,高性能)或数据库去重表,key=消息ID,value=消费状态(1=已消费),过期时间=消息最大重试时间+1天(如7天)。
-
执行流程:
- 消费者接收消息,获取消息ID。
- Redis查询该消息ID是否存在,存在则返回消费成功(重复消费)。
- 不存在则执行业务逻辑,执行成功后,将消息ID存入Redis(设置过期时间),再手动ACK。
-
避坑:必须用“原子操作”(Redis的SETNX命令),避免并发场景下重复消费(如两个消费者同时查询到消息ID不存在,同时执行业务)。
-
-
-
方案2:基于业务唯一键去重(业务场景首选)
- 原理:利用业务自身的唯一标识(如订单号、支付流水号),判断业务是否已执行,无需依赖消息ID。
- 适用场景:下单、支付、短信发送等有业务唯一键的场景。
- 实例:订单支付消息,用支付流水号作为唯一键,消费前查询数据库,若该流水号已支付,则直接返回成功;未支付则执行支付逻辑。
- 落地细节:数据库建唯一索引(如支付流水号唯一索引),避免重复执行时插入重复数据,捕获唯一键冲突异常,返回消费成功。
-
方案3:基于状态机判断(订单/支付场景)
- 原理:通过业务状态流转,避免重复执行(如订单状态:待支付→已支付→已发货,已支付的订单无法重复支付)。
- 执行流程:消费消息前,查询订单当前状态,若状态不允许执行(如已支付),则直接返回成功;若允许(如待支付),则执行业务,更新状态。
面试避坑:不要说“避免重复消费”,要明确“重复消费无法避免,核心是保证幂等性”,这是面试官想听到的专业回答。
3. 消息顺序性(部分场景必保,非所有场景都需要)
核心场景:需要保证消息按发送顺序执行(如订单流程:创建订单→支付订单→发货订单→完成订单),若顺序错乱,会导致业务异常(如先发货再支付)。
非核心场景:无需保证顺序(如短信通知、日志记录),顺序错乱不影响业务。
(1)消息顺序错乱的原因
- 生产者:多线程发送消息,消息发送顺序和业务顺序不一致。
- MQ:消息被分配到不同队列,不同队列的消息消费顺序独立,导致整体顺序错乱。
- 消费者:多线程消费消息,即使消息按顺序到达,多线程处理也会导致顺序错乱。
(2)顺序性保证方案(RocketMQ落地,分步骤)
-
生产者端:保证消息按业务顺序发送
-
单线程发送:核心顺序场景,生产者用单线程发送消息,避免多线程乱序。
-
同一业务数据发送到同一队列:
- RocketMQ配置:通过MessageQueueSelector,按业务唯一键(如订单ID)哈希取模,将同一订单的所有消息发送到同一个队列。
- 代码示例:producer.send(message, new MessageQueueSelector() { @Override public MessageQueue select(List mqs, Message msg, Object arg) { // arg为订单ID,按订单ID哈希取模,分配到同一队列 Long orderId = (Long) arg; int index = orderId.hashCode() % mqs.size(); return mqs.get(index); } }, orderId);
-
-
MQ端:保证队列内消息顺序
- MQ队列是“FIFO(先进先出)”结构,同一队列内的消息,按发送顺序存储和推送,无需额外配置。
- 避坑:不要给顺序消息的队列设置过多分区,分区越多,顺序错乱的概率越高(核心顺序场景,队列数量=消费者数量)。
-
消费者端:保证消息按顺序处理
- 单线程消费:同一队列只分配给一个消费者,消费者用单线程处理消息,避免多线程乱序。
- RocketMQ配置:设置consumer.setConsumeThreadMin(1)、consumer.setConsumeThreadMax(1)(单线程消费),同时设置MessageModel.CLUSTERING(集群模式),确保同一队列只被一个消费者消费。
- 避坑:单线程消费会降低并发能力,仅在“必须保证顺序”的场景使用(如订单流程),非顺序场景用多线程消费提升性能。
(3)取舍:顺序性 vs 并发性能
保证顺序性必然会牺牲并发性能(单线程发送/消费),因此只有核心业务场景(如订单、支付)需要保证顺序,非核心场景优先追求并发性能,无需保证顺序。
4. 消息堆积(线上高频故障,快速排查+解决)
定义:消息生产速度远大于消费速度,或消费者宕机,导致MQ中消息堆积过多(如堆积10万+条),若不及时处理,会导致MQ磁盘占满、消息过期、业务延迟。
(1)消息堆积的核心原因
- 消费速度<生产速度:消费者处理能力不足(如单线程消费、业务逻辑耗时过长)。
- 消费者故障:消费者集群宕机、消费者线程池满、业务逻辑报错导致无法消费。
- 消息突发:秒杀、大促等场景,消息量瞬间暴涨,超出消费者处理能力。
- 消费逻辑优化不及时:业务逻辑新增耗时操作(如新增数据库查询),导致消费速度下降。
(2)消息堆积的排查步骤(线上实操,直接用)
- 第一步:查看堆积情况:通过MQ控制台(如RocketMQ Console),查看队列的消息堆积数量、堆积时间,定位堆积的队列和消费者。
- 第二步:排查消费者状态:查看消费者是否正常运行、线程池是否满、是否有报错日志(如数据库连接超时、业务逻辑异常)。
- 第三步:分析堆积原因:是消费速度不足,还是消费者故障,还是消息突发。
- 第四步:快速处理:根据原因采取对应措施,优先恢复消费,再优化性能。
(3)消息堆积的解决方案(按优先级排序)
-
紧急处理:快速提升消费能力(优先恢复)
-
增加消费者实例:水平扩容,新增消费者节点,分担消费压力(RocketMQ集群模式下,新增消费者会自动分配队列)。
-
提高消费者并发:
- 非顺序场景:增加消费者线程池大小(RocketMQ配置:setConsumeThreadMin(10)、setConsumeThreadMax(20))。
- 顺序场景:增加队列数量(队列数量=消费者数量),每个消费者单线程消费,提升整体并发。
-
临时关闭非核心业务:若消费者同时处理核心和非核心消息,临时关闭非核心消息消费,集中资源处理堆积消息。
-
-
长期优化:提升消费速度(避免再次堆积)
-
优化消费逻辑:
- 减少数据库查询次数(如批量查询、缓存热点数据)。
- 避免耗时操作(如远程调用、大文件处理),将耗时操作异步化。
- 批量消费:开启RocketMQ批量消费(setConsumeMessageBatchMaxSize(100)),一次消费100条消息,减少消费次数。
-
优化MQ配置:
- 增大消费者拉取数量(setPullBatchSize(100)),一次拉取更多消息,减少拉取次数。
- 调整消息重试机制,避免无效重试(如非核心消息,重试3次后直接进入死信队列)。
-
-
兜底方案:处理过期/无效消息
- 删除无效消息:若堆积的消息是过期消息(如超过24小时的订单通知),直接删除,无需消费。
- 消息迁移:将堆积的消息迁移到临时队列,后续慢慢消费,不影响核心业务。
(4)避坑点
- 不要盲目增加线程数:线程数过多会导致数据库连接池满、网络拥堵,反而降低消费速度,需根据消费者处理能力合理设置。
- 顺序场景堆积:不能通过增加线程数解决,只能增加队列和消费者实例,保证每个队列单线程消费。
- 定期监控:通过Prometheus+Grafana监控消息堆积数量,设置告警(如堆积超过1000条,触发告警),提前发现问题。
四、RocketMQ核心特性(面试+工作重点,落地配置)
RocketMQ是国内互联网主流MQ,核心特性(事务消息、延迟消息、死信队列)是面试必问,也是工作中常用,重点掌握原理+落地配置。
1. 事务消息(分布式事务神器,核心重点)
(1)核心作用
解决“本地事务执行”和“消息发送”的一致性问题,避免“本地事务成功但消息未发送”或“消息发送成功但本地事务失败”,实现分布式事务最终一致性。
适用场景:下单、支付、库存扣减等核心业务,需要跨服务保证数据一致性。
(2)核心原理(4步流程)
-
发送半消息:生产者发送一条“半消息”到RocketMQ,半消息是“不能被消费者消费”的消息,MQ接收后返回ACK,确认半消息发送成功。
-
执行本地事务:生产者执行本地事务(如下单:创建订单、扣减库存),记录本地事务状态。
-
提交/回滚消息:
- 本地事务执行成功:生产者向MQ发送“提交消息”指令,MQ将半消息转为“可消费消息”,推送给消费者。
- 本地事务执行失败:生产者向MQ发送“回滚消息”指令,MQ删除半消息,消费者不会收到该消息。
-
事务回查(兜底机制) :若生产者发送提交/回滚指令失败(如网络波动、生产者宕机),MQ会定期(默认1分钟)向生产者发起“事务回查”,查询本地事务状态,根据状态执行提交/回滚。
(3)RocketMQ落地配置(核心代码片段)
-
-
生产者配置事务消息:
- 创建事务生产者:TransactionMQProducer producer = new TransactionMQProducer("transaction_group"); producer.setNamesrvAddr("127.0.0.1:9876"); // 设置事务监听器(处理本地事务和回查) producer.setTransactionListener(new TransactionListener() { // 执行本地事务 @Override public LocalTransactionState executeLocalTransaction(Message msg, Object arg) { try { // 执行本地事务(创建订单、扣减库存) orderService.createOrder(); // 本地事务成功,返回COMMIT_MESSAGE return LocalTransactionState.COMMIT_MESSAGE; } catch (Exception e) { // 本地事务失败,返回ROLLBACK_MESSAGE return LocalTransactionState.ROLLBACK_MESSAGE; } } // 事务回查 @Override public LocalTransactionState checkLocalTransaction(MessageExt msg) { // 查询本地事务状态(如查询订单表,判断订单是否创建成功) String orderId = new String(msg.getBody()); Order order = orderDao.selectById(orderId); if (order != null) { return LocalTransactionState.COMMIT_MESSAGE; } else { return LocalTransactionState.ROLLBACK_MESSAGE; } } }); producer.start();
-
-
-
关键配置:
- 事务组名称:同一业务的事务消息,使用同一个事务组,确保回查正常。
- 回查次数:默认15次,每次回查间隔递增(1s、5s、10s...),超过次数未确认,消息自动回滚。
-
(4)避坑点
- 本地事务必须幂等:事务回查时,可能会重复执行本地事务,需保证幂等性(如订单创建用唯一订单号去重)。
- 回查逻辑必须可靠:回查时要能准确查询本地事务状态,避免因回查失败导致消息无法提交/回滚。
- 不要用事务消息处理长事务:本地事务执行时间不宜过长(<500ms),避免MQ回查超时。
2. 延迟消息(非常实用,无需定时任务)
(1)核心作用
消息发送到MQ后,不立即被消费者消费,而是延迟指定时间后,才推送给消费者,替代定时任务(如订单超时未支付自动取消、短信延迟发送)。
优势:比定时任务更高效、更可靠,避免定时任务集群部署的复杂性,减少数据库查询压力。
(2)RocketMQ延迟级别(固定)
RocketMQ不支持自定义延迟时间,只支持固定的延迟级别,共18级,核心常用级别:
- 1级:1s,2级:5s,3级:10s,4级:30s,5级:1min
- 6级:2min,7级:3min,8级:4min,9级:5min,10级:10min
- 11级:20min,12级:30min,13级:1h,14级:2h
落地细节:若需要自定义延迟时间(如15min),可选择最接近的级别(如11级20min),或在消费者端判断延迟时间,达到目标时间再执行业务。
(3)落地配置(代码片段)
-
生产者发送延迟消息:
- Message message = new Message("delay_topic", "tag1", "订单超时取消".getBytes()); // 设置延迟级别(如5级,延迟1分钟) message.setDelayTimeLevel(5); // 发送消息 producer.send(message);
-
消费者消费延迟消息:
- 无需额外配置,消费者正常消费即可,MQ会在延迟时间到后,将消息推送给消费者。
(4)适用场景
- 订单超时未支付自动取消(延迟30分钟)
- 短信延迟发送(如下单成功后5分钟发送感谢短信)
- 任务超时提醒(如工单1小时未处理,发送提醒消息)
3. 死信队列(DLQ,异常消息处理)
(1)核心作用
消息经过多次重试(默认5次)后,仍消费失败,会被MQ放入“死信队列”,用于人工排查、日志记录、恢复重试,避免无效重试占用资源,也避免消息丢失。
死信队列是“特殊的主题”,命名规则:%DLQ%+消费者组名称,只有该消费者组能消费死信队列的消息。
(2)消息进入死信队列的条件(3个,必背)
- 消息重试次数耗尽(默认5次,可配置)。
- 消息被消费者拒绝消费(返回RECONSUME_LATER,且不重试)。
- 消息过期(设置了消息过期时间,且过期未被消费)。
(3)落地配置与处理流程
-
配置死信队列:
- RocketMQ默认开启死信队列,无需额外配置,只需设置消息重试次数(consumer.setMaxReconsumeTimes(5))。
- 死信队列的消息会持久化,默认保存7天,可通过配置修改保存时间。
-
死信消息处理流程(线上实操) :
- 通过RocketMQ Console,查看死信队列的消息,分析消费失败原因(如业务逻辑报错、数据库连接超时)。
- 修复问题(如修复业务bug、恢复数据库连接)。
- 手动重试:将死信消息重新发送到原队列,或发送到临时队列,让消费者重新消费。
- 兜底处理:若无法修复,手动处理消息(如手动执行业务逻辑),避免消息积压。
(4)避坑点
- 不要忽略死信队列:死信消息是线上问题的重要线索,需定期查看,及时处理,避免死信队列堆积。
- 不要无限重试:设置合理的重试次数(5-10次),避免无效重试占用MQ和消费者资源。
- 死信消息需幂等:重新消费死信消息时,需保证幂等性,避免重复执行。
五、MQ高可用架构
MQ作为分布式系统的核心组件,必须保证高可用(可用性99.99%),避免MQ宕机导致整个系统瘫痪,重点讲解RocketMQ和Kafka的高可用架构。
1. RocketMQ高可用架构(多Master多Slave)
-
核心架构组件:
-
NameServer:无状态,集群部署(至少2个节点),负责服务发现和路由管理,生产者/消费者通过NameServer获取Broker地址。
-
Broker:核心节点,分为Master和Slave,多Master多Slave部署(至少2个Master,每个Master对应1个Slave)。
- Master:负责接收生产者发送的消息,写入磁盘(持久化),同步消息到Slave。
- Slave:同步Master的消息,负责读消息(消费者从Slave拉取消息),Master宕机后,Slave自动切换为Master。
-
-
高可用保障机制:
- 集群部署:NameServer、Broker均集群部署,无单点故障。
- 主从同步:Master和Slave同步消息(同步同步/SYNC_MASTER),确保消息不丢失。
- 故障自动切换:Master宕机后,Slave自动切换为Master,消费者无需修改配置,继续消费。
- 持久化:消息存储到磁盘,即使Broker宕机,重启后消息不丢失。
-
落地部署建议(中小系统) :
- NameServer:2个节点(部署在不同服务器)。
- Broker:2个Master + 2个Slave(每个Master对应1个Slave,部署在不同服务器)。
- 刷盘模式:Master用同步刷盘(SYNC_FLUSH),Slave用异步刷盘(ASYNC_FLUSH)。
- 同步方式:Master和Slave用同步同步(SYNC_MASTER)。
2. Kafka高可用架构(分区副本)
-
核心架构组件:
-
Broker:集群部署,每个Broker是一个节点,负责存储消息、处理生产者/消费者请求。
-
Topic:消息主题,每个Topic分为多个分区(Partition),分区是Kafka的最小存储单元,水平扩容的核心。
-
副本(Replica):每个分区有多个副本(默认3个),分为Leader副本和Follower副本。
- Leader副本:负责处理读写请求,一个分区只有一个Leader。
- Follower副本:同步Leader的消息,Leader宕机后,Follower选举为新的Leader。
-
Zookeeper:负责Kafka集群的元数据管理、Leader选举、故障检测。
-
-
高可用保障机制:
- 分区副本:每个分区多副本,Leader宕机后,Follower自动选举为Leader,消息不丢失。
- 集群部署:Broker集群部署,无单点故障。
- 持久化:消息存储到分区日志文件,即使Broker宕机,重启后消息不丢失。
六、面试高频15题(必背,直接口述)
- 为什么要用MQ?(分异步、解耦、削峰、最终一致性,结合场景说)
- RocketMQ、Kafka、RabbitMQ的区别?选型依据是什么?
- 如何保证消息不丢失?(分生产者、MQ、消费者三个环节)
- 消息重复消费的原因是什么?如何解决?(核心是幂等性,分方案说)
- 如何保证消息顺序性?(分生产者、MQ、消费者,说明顺序性和并发的取舍)
- 消息堆积的原因是什么?如何排查和解决?(线上实操步骤)
- RocketMQ事务消息的原理是什么?(4步流程+回查机制)
- RocketMQ延迟消息的级别有哪些?适用场景?
- 死信队列是什么?消息进入死信队列的条件?如何处理死信消息?
- RocketMQ的高可用架构是什么?(NameServer+多Master多Slave)
- Kafka的高可用架构是什么?(分区副本+Leader选举)