Skip to content

消息中间件面试题库 ​

生成时间:2026-05-22 22:46


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

【面试题】1. Kafka 的 ISR 机制是什么?为什么需要 ISR 而不是简单的主从同步? ​

【标准答案】ISR 全称 In-Sync Replicas,是 Kafka 中与 Leader 副本保持同步的副本集合。

核心机制:

  1. 每个 Partition 有一个 Leader 和多个 Follower,ISR 动态维护"同步中的副本"
  2. 副本同步有延迟阈值(replica.lag.time.max.ms),超时会被踢出 ISR
  3. 只有 ISR 中的副本才有资格被选为新 Leader

为什么需要 ISR:

  • 传统主从同步要求所有副本都同步,一个慢节点拖慢整体性能
  • ISR 是一种"折中方案":既保证可靠性(ISR 内副本数据一致),又保证性能(不等待所有副本)
  • 配合 acks 参数,用户可以选择acks=all时只等 ISR 副本确认,而非全部副本

实际工作中,我会监控 ISR 收缩情况,频繁收缩说明节点或网络有问题。


【面试题】2. RabbitMQ 如何实现延迟消息?两种方案对比 ​

【标准答案】RabbitMQ 有两种主流方案:

方案一:TTL + 死信队列(DLX)

  • 核心思路:消息设置 TTL,过期后进入死信队列,消费者监听死信队列
  • 优点:原生支持,无需插件
  • 缺点:消息阻塞问题——队列是 FIFO,前面的消息未过期会阻塞后面消息的处理

方案二:rabbitmq_delayed_message_exchange 插件

  • 核心思路:消息在 Exchange 延迟,到时间后再路由到队列
  • 优点:解决消息阻塞问题,性能更好
  • 缺点:需要安装插件,当前版本只支持持久化到磁盘(有性能开销)

生产环境建议:

  • 延迟时间固定且短(如 5 分钟内)用 TTL + DLX
  • 延迟时间不固定或较长,用延迟消息插件
  • 延迟消息量大、性能要求高,考虑 RocketMQ(原生支持 18 个延迟等级)

【面试题】3. RocketMQ 为什么用 NameServer 而不是 Zookeeper? ​

【标准答案】这是架构设计的经典取舍:

NameServer 特点:

  1. 无状态,节点间互不通信,各自独立运行
  2. 每个 NameServer 都有完整的路由信息(Broker 地址、Topic 分区信息)
  3. Producer/Consumer 同时连接所有 NameServer

不选 Zookeeper 的原因:

  1. AP 优于 CP:RocketMQ 更看重可用性,ZK 强一致性在节点宕机时可能影响服务
  2. 降低复杂度:ZK 选举、会话管理增加了运维成本
  3. 路由信息不需要强一致性:Topic 路由短暂不一致不会导致数据丢失,最多消息发送失败后重试

实际影响:

  • Broker 向所有 NameServer 发送心跳(30秒),某个 NameServer 挂了不影响服务
  • Producer 会缓存路由信息,即使所有 NameServer 宕机,短时间内仍可工作

这是我理解 RocketMQ 高可用的核心设计之一。


【面试题】4. 消息积压怎么处理?紧急情况下的应急预案 ​

【标准答案】消息积压是生产环境最常见的问题,分三个阶段处理:

紧急排查(5 分钟内):

  1. 定位原因:消费者挂了?处理慢?下游服务超时?
  2. 查看监控:消费者 lag、CPU/内存、下游服务响应时间

短期应急(30 分钟内):

  1. 临时扩容消费者:增加消费者实例数量(注意:Kafka 一个分区只能被一个消费者消费,需要先增加分区)
  2. 批量消费:Kafka 设置 max.poll.records 增大,RocketMQ 设置 consumeMessageBatchMaxSize
  3. 异步处理:消息体先落本地表,异步批量处理,减轻下游压力
  4. 丢弃非核心消息:设置消息过期时间,过期自动删除(慎用)

长期优化:

  1. 消费者性能优化:数据库批量插入、异步非阻塞处理
  2. 动态扩缩容:K8s HPA 根据积压指标自动扩容
  3. 报警机制:积压超过阈值触发报警,提前干预

我之前遇到积压 500 万条的情况,通过临时扩容 10 倍消费者 + 批量消费,30 分钟清空。


【面试题】5. Kafka 如何保证消息顺序性? ​

【标准答案】Kafka 只能保证分区内顺序,不能保证全局顺序。实现方案:

方案一:单分区(简单但有性能瓶颈)

  • Topic 只设置一个分区,所有消息进同一个分区,天然有序
  • 缺点:并发度低,吞吐量受限

方案二:分区键(生产推荐)

  • Producer 发送消息时指定 key(如订单 ID),相同 key 的消息路由到同一分区
  • 核心配置:partitioner.class 或默认 hash(key) % 分区数
  • 注意:key 要有足够分散性,避免数据倾斜

方案三:消费端串行处理

  • 消费者单线程处理,或针对同一 key 的消息串行处理
  • 可以用内存队列 + Hash 分组,保证同 key 消息串行

实际场景举例:

  • 订单状态变更(创建→支付→发货),用订单 ID 作为 key
  • 同一订单的所有消息进入同一分区,严格有序

坑点:消费者重平衡时可能导致短暂乱序,建议关闭自动提交,处理完再提交 offset。


【面试题】6. RabbitMQ 消息持久化包括哪些部分?如何避免消息丢失? ​

【标准答案】RabbitMQ 持久化三层:

1. Exchange 持久化

  • 声明时设置 durable=true
  • 只保证元数据(Exchange 定义)不丢失

2. Queue 持久化

  • 声明时设置 durable=true
  • 保证队列元数据不丢失

3. Message 持久化

  • 发送消息时设置 delivery_mode=2
  • 消息写入磁盘

完整防丢失链路:

生产端 Confirm 模式 → 消息持久化 → 集群镜像队列 → 消费端手动 ACK

配置要点:

  1. 生产端开启 Confirm:channel.confirmSelect(),配合回调确认消息到达 Broker
  2. Exchange/Queue/Message 三层都持久化
  3. 集群开启镜像队列(ha-mode: all)或仲裁队列
  4. 消费端关闭自动 ACK,处理完业务再手动 ACK

坑点:

  • 持久化有性能开销,磁盘 IO 是瓶颈
  • 消息刚写入 PageCache 还未刷盘时宕机仍可能丢失(可配置同步刷盘,但性能更差)
  • 镜像队列会同步到所有节点,网络开销大

【面试题】7. RocketMQ 事务消息的原理是什么?如何实现分布式事务? ​

【标准答案】RocketMQ 事务消息采用"半消息 + 回查"机制:

完整流程:

  1. 发送半消息:Producer 发送消息到 Broker,消息标记为"不可消费",消费者看不到
  2. 执行本地事务:Producer 执行本地数据库操作(如扣款)
  3. 提交/回滚:本地事务成功则提交消息,失败则回滚(消息删除)
  4. 事务回查:如果 Producer 宕机未提交/回滚,Broker 定时回查 Producer 本地事务状态

关键设计:

  • 半消息存储在特殊 Topic:RMQ_SYS_TRANS_HALF_TOPIC
  • 回查机制保证最终一致性:Producer 恢复后仍能正确处理
  • 默认回查次数 15 次,超过进入死信队列

代码示例:

java
// 发送事务消息
TransactionMQProducer producer = new TransactionMQProducer("group");
producer.setTransactionListener(new TransactionListener() {
    @Override
    public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
        // 执行本地事务
        return LocalTransactionState.COMMIT_MESSAGE;
    }
    
    @Override
    public LocalTransactionState checkLocalTransaction(MessageExt msg) {
        // 回查本地事务状态
        return LocalTransactionState.COMMIT_MESSAGE;
    }
});

适用场景:

  • 订单创建后发优惠券(订单成功→优惠券发放)
  • 支付成功后通知发货

对比本地消息表:RocketMQ 事务消息无需额外的消息表,由 MQ 管理事务状态。


【面试题】8. Kafka 零拷贝原理是什么?为什么这么快? ​

【标准答案】Kafka 高性能的核心之一:零拷贝 + 顺序写

零拷贝(Zero Copy):

  • 传统方式:磁盘 → 内核缓冲区 → 用户缓冲区 → 内核 Socket 缓冲区 → 网卡
  • 4 次拷贝,4 次上下文切换
  • 零拷贝:通过 sendfile 系统调用,磁盘 → 内核缓冲区 → 网卡(DMA 直接传输)
  • 2 次拷贝,2 次上下文切换

顺序写:

  • Kafka 追加写入 CommitLog,磁盘顺序写速度接近内存(600MB/s vs 100MB/s 随机写)
  • 消费者按 offset 顺序读取,充分利用 PageCache

批量与压缩:

  • Producer 批量发送(batch.size=16KB 默认)
  • 支持 GZIP/Snappy/LZ4/ZSTD 压缩,减少网络传输

PageCache 复用:

  • 写入数据先到 PageCache,异步刷盘
  • 消费者读 PageCache,大概率命中缓存(热点数据)

实际优化建议:

  • 日志目录用独立磁盘,避免随机 IO 竞争
  • batch.size 根据消息大小调整(大消息用更大 batch)
  • 压缩算法选 ZSTD,压缩率和速度平衡最好

【面试题】9. 消息中间件如何选型?RabbitMQ vs Kafka vs RocketMQ ​

【标准答案】选型核心指标:吞吐量、延迟、可靠性、功能、生态

RabbitMQ:

  • 特点:低延迟(微秒级)、功能丰富(延迟队列、死信队列、灵活路由)
  • 吞吐量:中等(万级 QPS)
  • 协议:AMQP 标准,多语言支持好
  • 适用场景:业务系统解耦、延迟任务、复杂路由需求
  • 缺点:集群做大规模成本高、消息堆积能力弱

Kafka:

  • 特点:高吞吐(百万级 QPS)、持久化强、生态完善
  • 延迟:毫秒级(比 RabbitMQ 慢)
  • 协议:自定义 TCP 协议
  • 适用场景:日志收集、大数据管道、实时计算、消息堆积场景
  • 缺点:功能相对简单(无延迟队列)、依赖 Zookeeper(新版去掉)

RocketMQ:

  • 特点:高吞吐、低延迟、事务消息、延迟消息
  • 吞吐量:十万级 QPS
  • 功能:事务消息、延迟消息(18 等级)、消息过滤
  • 适用场景:金融交易、电商订单、分布式事务
  • 缺点:社区生态不如 Kafka、客户端支持语言较少

选型决策树:

  1. 日志/大数据场景 → Kafka
  2. 金融/电商/需要事务消息 → RocketMQ
  3. 传统业务系统/复杂路由 → RabbitMQ
  4. 超高吞吐(百万级)+ 需要事务 → RocketMQ + Kafka 组合

我们公司用 RocketMQ 做核心交易链路,Kafka 做日志收集,RabbitMQ 做定时任务。


【面试题】10. 消费端幂等性如何设计?几种方案对比 ​

【标准答案】消息重复是必然的(网络重试、消费者宕机重发),必须设计幂等。

方案一:数据库唯一索引

sql
-- 消息处理表
CREATE TABLE msg_process (
    msg_id VARCHAR(64) PRIMARY KEY,
    status TINYINT,
    create_time TIMESTAMP
);
  • 流程:消费消息 → 插入 msg_id → 成功则处理,失败(重复)则跳过
  • 优点:实现简单,强一致性
  • 缺点:数据库压力,需要定期清理历史数据

方案二:Redis SETNX

java
boolean processed = redis.setnx("msg:" + msgId, "1", 300); // 5分钟过期
if (!processed) return; // 已处理
  • 流程:Redis 判断是否处理过 → 未处理则加锁 → 处理业务 → 删除锁
  • 优点:高性能
  • 缺点:需要处理 Redis 宕机场景(兜底方案)

方案三:乐观锁(业务幂等)

sql
UPDATE order SET status = 'PAID' WHERE order_id = ? AND status = 'UNPAID';
  • 流程:利用业务状态机天然幂等
  • 优点:无需额外存储
  • 缺点:依赖业务逻辑,通用性差

方案四:多级缓存

  • Redis 过期时间短(5 分钟)+ 数据库持久化(长期)
  • 先查 Redis,没有再查数据库

生产建议:

  • 关键链路用数据库唯一索引 + Redis 双保险
  • 非关键链路只用 Redis(设置合理过期时间)
  • 消息 ID 用业务 ID(如订单号)而非 MQ 生成的 ID