Skip to content

本轮 消息中间件面试题库(带答案) ​

【面试题】1. RabbitMQ中如何保证消息不丢失?请从生产端、Broker端、消费端三个维度说明。 ​

【标准答案】消息丢失防护需要三端协同:

生产端:开启Confirm模式(confirm.select),消息到达队列后Broker返回ACK,配合回调机制确保投递成功。也可以配合事务机制,但性能较差,生产环境推荐Confirm。

Broker端:

  1. 消息持久化:队列声明durable=true,消息投递时delivery_mode=2
  2. Exchange和Queue都要持久化
  3. 镜像队列:集群环境下配置ha-mode=all,主节点故障自动切换

消费端:手动ACK,业务逻辑执行成功后再确认。如果自动ACK,消息拉取后消费者宕机就丢了。配置prefetchCount控制未ACK消息数量,防止消息积压在客户端。

核心思路:生产端确认投递、Broker持久化+高可用、消费端手动确认,三者缺一不可。


【面试题】2. Kafka的ISR机制是什么?为什么需要ISR?ISR收缩和扩容的触发条件是什么? ​

【标准答案】ISR(In-Sync Replicas)是Kafka分区中与Leader保持同步的副本集合,是Kafka数据可靠性和可用性的核心机制。

为什么需要ISR:

  1. 如果要求所有副本都写入才算成功,任何一个副本宕机整个分区就不可用,可用性太差
  2. 如果只要Leader写入就算成功,Leader故障可能导致数据丢失,可靠性不够
  3. ISR是一种折中:只需要ISR中的副本同步即可,既保证可靠性又兼顾可用性

ISR收缩条件(副本从ISR移除):

  • 副本与Leader的LEO(日志末端位移)差距超过replica.lag.time.max.ms(默认30秒)
  • 副本长时间未发送FetchRequest或FetchResponse

ISR扩容条件(副本加入ISR):

  • 追赶副本的LEO接近Leader的LEO,差距在可接受范围内
  • 由Controller协调更新ISR列表

关键参数:min.insync.replicas,表示ISR中最少需要多少副本,配合acks=all使用,ISR大小小于此值时生产者会抛异常。


【面试题】3. RocketMQ的事务消息是如何实现的?半消息是什么?如何保证事务最终一致性? ​

【标准答案】RocketMQ事务消息用于解决分布式事务问题,核心是半消息机制。

半消息:生产者发送消息到Broker后,消息暂存不能被消费,状态为"不可消费",这就是半消息。只有本地事务执行成功并发送Commit指令后,消息才对消费者可见。

实现流程:

  1. 生产者发送半消息到Broker
  2. Broker返回半消息写入成功
  3. 生产者执行本地事务
  4. 本地事务成功 → 发送Commit,消息变为可消费
  5. 本地事务失败 → 发送Rollback,消息被丢弃
  6. 如果Commit/Rollback未送达,Broker会回查生产者的本地事务状态(事务回查机制)

事务回查机制:

  • Broker定时扫描长时间未确认的半消息
  • 向生产者发起事务状态回查
  • 生产者根据本地事务执行结果返回Commit/Rollback
  • 默认回查15次,超过次数消息丢弃并记录日志

一致性保证:通过事务回查机制,即使Commit/Rollback丢失,最终也能保证消息状态与本地事务一致。适合对实时性要求不高的场景。


【面试题】4. 消息队列中如何保证消息的顺序性?RabbitMQ、Kafka、RocketMQ分别如何实现? ​

【标准答案】消息顺序性分为全局有序和分区有序。全局有序性能太差,生产环境通常用分区有序。

RabbitMQ:

  • 单队列单消费者:天然有序,但吞吐量低
  • 单队列多消费者:无法保证顺序,消息分发是轮询的
  • 实现方式:分区队列(多个队列),同一业务ID的消息路由到同一队列,每个队列单消费者

Kafka:

  • Topic是多分区,消息分发到不同分区无序
  • 实现方式:生产者指定分区Key(如订单ID),相同Key的消息进入同一分区
  • 分区内有序,但一个分区只能被消费组中的一个消费者消费
  • 注意:Consumer Group Rebalance会破坏顺序性

RocketMQ:

  • 支持全局有序和分区有序
  • 全局有序:单Topic单队列,性能差
  • 分区有序:MessageQueueSelector,按业务Key选择队列,相同Key的消息进入同一队列
  • 顺序消息发送时自动锁定队列,保证消费顺序

核心思路:相同业务ID的消息路由到同一分区/队列,该分区/队列单消费者处理。


【面试题】5. Kafka消费者组的Rebalance机制是什么?什么情况下会触发Rebalance?如何避免频繁Rebalance? ​

【标准答案】Rebalance是Consumer Group内消费者与分区重新分配的过程,直接影响消费组的可用性和吞吐量。

触发条件:

  1. 消费者加入或退出消费组
  2. 消费者宕机或Session超时(max.poll.interval.ms超时)
  3. Topic分区数变化(新增分区)
  4. 消费者主动取消订阅

Rebalance过程(协作模式):

  1. 消费者向Coordinator发送JoinGroup请求
  2. Coordinator选举Group Leader
  3. Leader制定分区分配方案(按策略:Range/RoundRobin/Sticky)
  4. 分配方案同步给所有消费者
  5. 消费者按分配方案拉取分区消息

频繁Rebalance的危害:

  • Rebalance期间消费暂停,所有消费者停止消费
  • 频繁Rebalance导致消费吞吐量剧烈波动
  • 可能导致消费延迟和积压

避免策略:

  1. 合理配置session.timeout.ms(默认10秒)和heartbeat.interval.ms(默认3秒)
  2. max.poll.interval.ms要大于单次poll处理的最大时长
  3. 业务处理逻辑要快,避免poll间隔超时
  4. 使用静态成员(group.instance.id),消费者短暂离线不触发Rebalance

【面试题】6. RocketMQ的延迟消息实现原理是什么?支持哪些延迟级别?与RabbitMQ的延迟队列有何区别? ​

【标准答案】RocketMQ延迟消息是消息发送后延迟一段时间再被消费,适合订单超时取消、定时任务等场景。

实现原理:

  1. Broker内置延迟主题(SCHEDULE_TOPIC_XXXX)
  2. 消息发送时设置延迟级别,Broker将消息暂存到延迟主题的对应队列
  3. Broker定时任务扫描到期消息
  4. 到期后将消息恢复到原主题,消费者即可消费

支持的延迟级别(18个等级):

1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h

不支持任意时间,只能选择预定义级别。

与RabbitMQ延迟队列的区别:

特性RocketMQRabbitMQ
实现方式内置延迟主题+定时扫描TTL+DLX 或 delayed_message_exchange插件
延迟精度固定级别(18个)任意时间(插件支持毫秒级)
性能高(原生支持)中等(依赖队列堆积)
消息堆积能力强(CommitLog)中等(内存/磁盘)

RocketMQ延迟消息性能高但不够灵活,RabbitMQ插件方案灵活但需要额外安装。


【面试题】7. 消息积压如何处理?请从生产端、Broker端、消费端三个维度说明应急方案和长期优化策略。 ​

【标准答案】消息积压是消息生产速度超过消费速度,导致消息在Broker中堆积。

应急处理方案:

消费端:

  1. 临时扩容消费者实例(Consumer Group内消费者数量 ≤ 分区数)
  2. 提高消费者并发度(线程池大小、prefetch数量)
  3. 快速消费模式:先批量拉取消息持久化到本地/数据库,业务处理异步化
  4. 跳过非核心消息:优先处理重要业务,次要消息临时丢弃或延迟处理

Broker端:

  1. 扩容集群节点,新增分区(Kafka新增分区后重新分配)
  2. 调整刷盘策略,临时异步刷盘提升吞吐
  3. 增加内存缓冲区,减少磁盘IO压力

生产端:

  1. 降低生产速率(限流、降级)
  2. 非核心业务暂停发送,优先保障核心业务

长期优化策略:

  1. 消费端性能优化:批量消费、异步处理、并行处理
  2. 架构优化:增加分区数、消费者数量
  3. 监控预警:设置积压阈值告警,提前发现问题
  4. 容量规划:根据业务峰值提前评估和扩容

关键:先止血(降生产、扩消费),再治本(优化消费性能、扩容架构)。


【面试题】8. Kafka的Exactly-Once语义是如何实现的?幂等Producer和事务有什么区别? ​

【标准答案】Kafka Exactly-Once确保消息"恰好被处理一次",是消息可靠性最高级别。

实现方式:

1. 幂等Producer(单分区幂等):

  • 生产者分配唯一PID(Producer ID)
  • 每条消息附加序列号(Sequence Number)
  • Broker检测到相同PID+序列号的消息,直接丢弃
  • 只能保证单分区幂等,跨分区无法保证
  • 配置:enable.idempotence=true

2. 事务(跨分区幂等):

  • 引入TransactionCoordinator协调事务
  • 生产者开启事务,发送多条消息到多个分区
  • 所有消息写入后,发送Commit或Abort
  • 消费者配置isolation.level=read_committed,只读取已提交的事务消息
  • 配置:transactional.id指定事务ID

两者区别:

特性幂等Producer事务
范围单分区跨分区、跨Topic
场景简单消息去重流处理、跨分区原子写入
性能高(无协调开销)中等(需要协调者)
配置enable.idempotence=true需配置transactional.id

流处理场景Exactly-Once: Kafka Streams通过事务+位移管理,实现"消费-处理-生产"全链路Exactly-Once。


【面试题】9. RabbitMQ的Exchange类型有哪些?分别适用什么场景?路由键和绑定键的区别是什么? ​

【标准答案】RabbitMQ Exchange负责消息路由,四种类型:

1. Direct Exchange:

  • 路由规则:精确匹配路由键(Routing Key)
  • 场景:点对点消息、单播
  • 示例:队列绑定键为"order.create",只有路由键为"order.create"的消息才进入队列

2. Fanout Exchange:

  • 路由规则:忽略路由键,广播到所有绑定队列
  • 场景:广播消息、群发通知
  • 示例:日志广播、配置更新通知所有服务

3. Topic Exchange:

  • 路由规则:通配符匹配(*匹配一个单词,#匹配多个单词)
  • 场景:多条件路由、灵活订阅
  • 示例:绑定键"order.*.create",路由键"order.user.create"匹配成功

4. Headers Exchange:

  • 路由规则:根据消息头属性匹配(忽略路由键)
  • 场景:复杂条件路由(性能较差,少用)

路由键 vs 绑定键:

  • 路由键(Routing Key):生产者发送消息时指定,标识消息的路由目标
  • 绑定键(Binding Key):队列绑定Exchange时指定,标识队列订阅规则

关系:Exchange根据路由键和绑定键的匹配规则,将消息路由到对应队列。Direct精确匹配、Topic通配匹配、Fanout忽略匹配。


【面试题】10. 如何设计一个百万级消息吞吐的消息系统?请从架构设计、性能优化、高可用三个维度说明。 ​

【标准答案】百万级消息吞吐系统设计:

架构设计:

  1. 分区架构:Topic分区分散到多个Broker,并行写入和消费
  2. 集群架构:多Broker集群,水平扩展
  3. 存储架构:顺序写+PageCache,避免随机IO
  4. 零拷贝:Kafka使用sendfile,RocketMQ使用mmap

性能优化:

生产端:

  • 批量发送:多条消息打包发送,减少网络开销
  • 异步发送:非阻塞发送,提升吞吐
  • 压缩:消息压缩(gzip/snappy/lz4),减少网络传输
  • 连接池:复用TCP连接,避免频繁建连

Broker端:

  • 顺序写磁盘,避免随机IO
  • PageCache热数据缓存,读命中内存
  • 零拷贝传输,减少用户态和内核态拷贝
  • 分区数合理规划(单Broker分区数 < 2000)
  • 内存池和对象复用,减少GC

消费端:

  • 批量拉取:一次拉取多条消息
  • 并发消费:线程池并行处理
  • 预取数量:合理配置prefetch,避免客户端积压

高可用设计:

  1. 多副本机制:每个分区多副本,主从切换
  2. ISR机制:确保副本同步
  3. Controller选举:Broker故障自动切换
  4. 监控告警:CPU、内存、磁盘、网络、积压监控
  5. 容灾:跨机房部署、异地多活

容量规划:

  • 单Kafka Broker吞吐:10万-20万QPS
  • 百万级需要5-10个Broker,配合合理分区数
  • SSD磁盘、万兆网络、充足内存

核心:水平扩展+顺序写+零拷贝+批量处理,四管齐下实现高吞吐。