本轮 消息中间件面试题库(带答案)
生成时间:2026-05-21 21:46 | 共10题
【面试题】1. Kafka的ISR机制是什么?为什么说它是Kafka可靠性的核心?
【标准答案】ISR就是In-Sync Replicas,即"同步副本集"。Kafka每个Partition有一个Leader和多个Follower,ISR就是那些和Leader保持同步的副本集合(包括Leader自身)。
核心机制:Follower通过不断从Leader拉取数据来追赶进度,如果一个Follower落后太多——超过了replica.lag.time.max.ms(默认10秒),就会被踢出ISR,等它追上来了再加回来。
为什么说它是可靠性核心?因为Kafka的acks机制依赖ISR:
acks=all时,消息必须被ISR里所有副本确认才算写入成功。这意味着只要ISR里至少有一个存活副本,数据就不会丢。- ISR是动态伸缩的——不健康的Follower自动剔除,不影响写入性能;恢复后再加入,又保证了冗余。
跟"等所有副本确认"相比,ISR方案兼顾了可靠性和可用性:不会因为一个慢节点卡住整个写入。面试时加一句:ISR + acks=all + min.insync.replicas=2,这是Kafka生产环境最推荐的可靠性黄金组合。
【面试题】2. RabbitMQ的死信队列(DLX)是什么?哪些场景下消息会进入死信队列?
【标准答案】死信队列就是RabbitMQ中无法被正常消费的消息的"归宿"。消息变成死信有三种情况:
- 消费端NACK且requeue=false:消费者显式拒绝且不重新入队
- 消息TTL过期:消息设置了过期时间,到时间了还没被消费
- 队列达到最大长度:队列设置了x-max-length,满了之后最早的消息被挤掉变成死信
怎么用?给队列设置x-dead-letter-exchange和x-dead-letter-routing-key参数,消息变成死信后自动路由到指定Exchange,再绑定到一个专门的死信队列。
经典应用场景:
- 延迟队列:消息设TTL → 过期进DLX → DLX绑定的队列就是延迟消费的队列,这是RabbitMQ做延迟任务的标准方案
- 异常处理:消费失败的消息不丢弃,进死信队列后人工排查或重试
- 订单超时取消:下单时发一条TTL=30分钟的消息,30分钟后进DLX触发取消逻辑
注意:死信队列不是RabbitMQ的内置队列类型,而是通过参数配置实现的机制,面试时别搞混。
【面试题】3. RocketMQ的事务消息是怎么实现的?和本地消息表方案比有什么优劣?
【标准答案】RocketMQ事务消息采用"两阶段提交+回查"机制:
执行流程:
- 生产者先发一条"半消息"(Half Message)到Broker,这条消息对消费者不可见
- Broker收到后半消息存储成功,返回确认
- 生产者执行本地事务
- 根据本地事务结果,发送Commit或Rollback给Broker
- Commit → 消息对消费者可见;Rollback → 消息删除
关键——回查机制:如果Broker没收到第4步的确认(比如生产者宕机了),Broker会定期回查生产者的本地事务状态,最多回查15次。生产者需要实现checkLocalTransaction接口返回事务状态。
对比本地消息表:
| 维度 | 事务消息 | 本地消息表 |
|---|---|---|
| 额外存储 | 不需要 | 需要业务库里建消息表 |
| 一致性保证 | Broker原生支持 | 依赖定时任务扫描补偿 |
| 侵入性 | 实现回查接口 | 写消息表+补偿逻辑 |
| 适用场景 | 用RocketMQ时优先 | 任何MQ都能用 |
面试总结:能用RocketMQ事务消息就用它,代码更干净,不需要额外表。如果公司用的不是RocketMQ,或者团队对RocketMQ不熟,本地消息表是更通用的兜底方案。
【面试题】4. 消息消费的幂等性怎么保证?生产环境中你用什么方案?
【标准答案】幂等性就是"同一条消息消费多次和消费一次效果一样"。为什么需要?因为网络抖动、Rebalance、生产者重试都可能导致消息重复投递。
三大主流方案:
唯一ID + 数据库去重表
- 每条消息带全局唯一ID(业务ID或消息ID)
- 消费前查去重表,存在就跳过
- 消费成功后把ID写入去重表
- 注意:查和写必须原子性,可以用唯一索引实现——插入成功才消费,主键冲突就跳过
Redis的SETNX
- 消费前用
SETNX message_id 1判断 - 成功则消费,失败则跳过
- 优势:性能高;劣势:Redis和数据库的一致性需要额外处理
- 实战技巧:SETNX设过期时间,避免KEY堆积
- 消费前用
业务状态机天然幂等
- 比如订单状态从"已支付"→"已发货",用
UPDATE order SET status='shipped' WHERE id=? AND status='paid' - 状态机流转是天然幂等的,重复执行影响行数为0
- 这是最高效的方案,能从业务设计上解决就不需要额外中间件
- 比如订单状态从"已支付"→"已发货",用
生产环境建议:优先用业务状态机保证幂等,覆盖不了的场景用数据库唯一索引兜底。Redis方案适合高并发但对一致性要求不那么严的场景。
【面试题】5. Kafka的Exactly-Once语义是怎么实现的?为什么说它是端到端的?
【标准答案】Kafka的Exactly-Once解决的是"消息既不丢也不重复"的问题,分三层实现:
1. 幂等Producer(单分区内幂等)
- 开启
enable.idempotence=true - Producer给每条消息分配一个Sequence Number
- Broker根据<PID, Partition, SeqNum>去重
- 作用:单分区内保证不重复,解决的是Producer重试导致的重复写入
2. 事务(跨分区的原子写入)
- Producer设置
transactional.id - 开启事务后,一批消息要么全部Commit,要么全部Abort
- 消费者设置
isolation.level=read_committed,只读已提交的消息 - 作用:保证"消费→处理→写入"整个流程的原子性
3. Consumer端的Offset提交
- 事务中把消息写入和Offset提交绑在一起,要么都成功要么都回滚
- 这就是"端到端"的含义——从消费到处理到再写入,全链路原子性
为什么叫端到端?因为不是只保证写入端幂等,而是从Consume → Process → Produce → Commit Offset整个闭环都保证Exactly-Once。以前的方案要么只管写入端(幂等Producer),要么只管消费端(手动提交Offset),Kafka事务把两端串起来了。
面试注意:Exactly-Once仅限于Kafka内部闭环,如果涉及到外部系统(写MySQL、调RPC),Kafka自身无法保证,需要业务侧配合幂等。
【面试题】6. RabbitMQ的Exchange四种类型有什么区别?分别适合什么场景?
【标准答案】RabbitMQ的Exchange负责消息路由,四种类型决定了不同的匹配规则:
1. Direct Exchange
- 精确匹配routing key
- 消息的routing key == 队列绑定的binding key → 投递
- 场景:点对点通信、日志级别路由(error/warn/info各绑一个队列)
2. Fanout Exchange
- 完全不看routing key,广播到所有绑定队列
- 场景:群发通知、事件广播(如下单后同时通知库存系统+积分系统+物流系统)
3. Topic Exchange
- 支持通配符匹配routing key:
*匹配一个单词,#匹配零或多个单词 - 例如:
order.*匹配order.create和order.cancel;order.#匹配order.create.success - 场景:多维度路由、发布订阅模式的灵活版本
4. Headers Exchange
- 不看routing key,匹配消息头(headers)的键值对
- x-match=all表示所有header都要匹配,x-match=any表示任一匹配即可
- 场景:需要多条件组合路由,且条件不适合编码到routing key中时
面试口诀:精确匹配用Direct,广播用Fanout,模式匹配用Topic,头匹配用Headers。90%的业务场景Direct和Topic就搞定了,Headers几乎用不到。
【面试题】7. 消息积压了怎么办?生产环境的应急处理流程是什么?
【标准答案】消息积压是消息中间件最常见的事故场景,处理分三步:
第一步:紧急止损
- 先确认消费端是否正常——是不是有服务挂了、数据库慢查、下游超时
- 如果是消费端故障,先修服务,这是治本
- 如果消费端没问题但就是处理不过来,进入第二步
第二步:快速扩容消费能力
- 增加消费者实例数(水平扩容)
- 但注意:Kafka中消费者数不能超过分区数,分区是消费并行度的上限
- 如果分区数不够,紧急扩分区(Kafka支持,但新分区只消费新消息)
- RabbitMQ可以动态增加队列和消费者
第三步:临时方案——转发+旁路消费
- 如果以上还不够,新建一个临时Topic/Queue,写一个转发程序把积压消息快速搬运过去
- 临时Topic多设几个分区,起一批消费者并行消费
- 消费完再恢复原架构
预防措施(面试加分):
- 监控消费延迟(Consumer Lag),设阈值告警
- 消费端做好限流降级,非核心逻辑异步化
- 合理规划分区数,留足消费并行度余量
- 消费逻辑避免慢操作(大事务、外部调用)
面试总结:先查因、再扩容、后旁路。别上来就加机器,先搞清楚为什么积压。
【面试题】8. Kafka为什么这么快?从存储和网络两个角度解释。
【标准答案】Kafka的高性能是架构设计的结果,不是某个单点优化:
存储层面:
- 顺序写:消息只追加到日志文件末尾,不随机写。磁盘顺序写速度可达600MB/s,随机写只有100KB/s,差了三个数量级。
- PageCache:Kafka大量利用操作系统的页缓存,写入时先写PageCache,由OS异步刷盘;读取时直接从PageCache命中,不经过用户态。
- 零拷贝:消费者拉消息时用
sendfile()系统调用,数据从磁盘→PageCache→网卡缓冲区,跳过了用户态拷贝,减少了2次数据拷贝和2次上下文切换。 - 批量写入+压缩:Producer攒一批消息再发,减少IO次数;支持gzip/snappy/lz4压缩,减少网络和磁盘IO。
网络层面:
- 批量发送:Producer的
linger.ms和batch.size参数控制攒批,减少网络请求次数 - Reactor网络模型:Broker用1个Acceptor线程+多个Processor线程处理网络连接,非阻塞NIO
- 分区并行:Topic分多个Partition,不同Partition可被不同Broker处理,天然并行
一句话总结:顺序写+零拷贝+PageCache解决了磁盘IO瓶颈,批量+压缩+分区并行解决了网络和吞吐瓶颈。这不是某个优化点,而是一整套为日志型数据设计的架构选择。
【面试题】9. RabbitMQ、Kafka、RocketMQ三个怎么选?什么场景用什么?
【标准答案】选型看三个维度:吞吐量、功能丰富度、运维复杂度。
RabbitMQ:
- 吞吐量:万级TPS
- 优势:功能最丰富,Exchange路由灵活、死信队列、延迟队列开箱即用,管理界面友好
- 劣势:吞吐量有上限,Erlang技术栈小众,集群扩容麻烦
- 适用:中小规模业务系统、需要复杂路由、对吞吐量要求不极端的场景
- 典型场景:订单状态变更通知、异步任务调度、企业内部系统集成
Kafka:
- 吞吐量:百万级TPS
- 优势:极致吞吐、分布式架构天然水平扩展、生态完善(Streams/Connect/KSQL)
- 劣势:功能相对简单,没有延迟消息、没有死信队列原生支持、运维较重
- 适用:大数据场景、日志收集、流式计算、高吞吐事件流
- 典型场景:用户行为日志采集、实时数仓管道、消息量巨大的事件驱动架构
RocketMQ:
- 吞吐量:十万级TPS
- 优势:事务消息、延迟消息、消息过滤(Tag/SQL92)等企业级功能齐全,Java技术栈好维护
- 劣势:社区不如Kafka活跃,生态不如Kafka丰富
- 适用:电商/金融等对消息可靠性要求高的业务场景
- 典型场景:分布式事务消息、订单超时取消、金融级消息通知
面试一句话:日志流式数据选Kafka,复杂路由中小规模选RabbitMQ,电商金融高可靠选RocketMQ。没有银弹,看业务场景。
【面试题】10. Kafka Consumer Group的Rebalance机制是什么?有什么问题?怎么优化?
【标准答案】Rebalance是Consumer Group内的分区重分配过程——当一个消费者加入或离开Group时,Kafka需要把分区重新分配给存活的消费者。
触发条件:
- 消费者加入Group(新增实例)
- 消费者离开Group(宕机或主动退出)
- 订阅的Topic分区数变化
- 消费者心跳超时(
session.timeout.ms)或处理超时(max.poll.interval.ms)
Rebalance的问题:
- Stop-The-World:Rebalance期间所有消费者停止消费,造成短暂消费暂停
- 重复消费:Rebalance后Offset可能回退,导致消息重复
- 频繁Rebalance:消费者处理慢导致max.poll.interval.ms超时,被踢出后又触发Rebalance,形成恶性循环
- 分配不均:默认的Range分配器可能导致前面的消费者分到更多分区
优化方案:
调参避免误触发
session.timeout.ms适当调大(如25s),避免网络抖动误判heartbeat.interval.ms设为session.timeout.ms的1/3max.poll.interval.ms根据业务处理时间调大(如5分钟)
使用Sticky分配器
partition.assignment.strategy=org.apache.kafka.clients.consumer.StickyAssignor- Rebalance时尽量保持原有分配不变,减少分区迁移
Kafka 2.4+的Cooperative Sticky协议
- 增量Rebalance,只迁移需要变动的分区
- 不再Stop-The-World,其余消费者正常消费
- 配置:
partition.assignment.strategy=org.apache.kafka.clients.consumer.CooperativeStickyAssignor
消费者健康检查
- 业务处理和心跳线程分离,长时间处理不影响心跳
- 避免在消费线程做耗时操作
面试总结:Rebalance是Consumer Group的"双刃剑"——保证故障恢复但也带来暂停。优化核心是"减少触发+减少影响",Sticky和Cooperative协议是技术手段,参数调优是兜底手段。