Skip to content

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

生成时间:2026-05-21 23:20


【面试题】1. Kafka 的日志压缩(Log Compaction)是什么?适用什么场景?和日志删除策略有什么区别? ​

【标准答案】

Log Compaction(日志压缩) 是 Kafka 的一种日志保留策略(另一种是 Log Deletion)。

核心原理:

  • 以 Key 为维度,只保留每个 Key 的最后一个值(最新状态)
  • 后台 Compact 线程扫描日志,删除相同 Key 的旧值
  • 日志头(Head)不压缩,只压缩日志尾(Tail)

与 Log Deletion 的区别:

维度Log DeletionLog Compaction
触发条件时间(retention.ms)或大小(retention.bytes)Key 重复,保留最新值
数据保留整段删除按 Key 去重保留最新
适用场景事件流、日志收集状态存储、KV 快照
例子用户点击日志用户最新手机号、地址

适用场景:

  • 数据库变更日志(CDC),只需要最新状态
  • KV 存储快照(如 Cache 预热)
  • Stream 的 State Store(Kafka Streams 内部使用)

配置方式:

properties
cleanup.policy=compact
min.compaction.lag.ms=5000  # 消息至少保留 5s 再参与压缩

面试加分点:

  • Compact 后的 Topic 可以作为 KV 数据库使用(通过 read_committed 隔离级别)
  • 压缩不是实时触发的,有延迟

【面试题】2. RabbitMQ 的流量控制机制是怎样的?Credit-based 流控原理是什么? ​

【标准答案】

RabbitMQ 有两层流量控制,防止生产者打爆 Broker。

第一层:Erlang 进程内存限制

  • vm_memory_high_watermark = 0.4(默认 40% 内存)
  • 超过阈值 → 阻塞所有生产者连接
  • 内存回落到阈值以下 → 恢复

第二层:Disk 磁盘空间限制

  • disk_free_limit = 1GB(默认 50MB 或 1.5 倍内存)
  • 磁盘不足 → 阻塞生产者

Credit-based 流控(核心机制):

  • 不是全局限流,而是基于 Channel 的信用值
  • 每个 Connection 有 Credit 值(默认 200)
  • 生产者每发一条消息,Credit -1
  • 消费者每 ack 一条,Credit +1
  • Credit = 0 时,生产者被阻塞
生产者 → [Credit > 0] → 发送消息 → Credit--
消费者 → 消费 → Ack → Credit++
Credit=0 → 生产者暂停 → 消费者继续消费 → Credit 恢复 → 生产者恢复

为什么不用简单的 Rate Limiting?

  • 全局限流不公平,慢消费者会拖累快生产者
  • Credit 机制是"背压(Backpressure)"模型,天然公平

生产调优:

ini
# rabbitmq.conf
total_connections = 1024
channel_max = 2047
vm_memory_high_watermark.relative = 0.4
disk_free_limit.relative = 1.5

面试加分点:

  • Credit 机制是 RabbitMQ 高可靠的核心设计之一
  • 如果消费者挂了,Credit 不恢复,生产者自然被限流,避免消息堆积

【面试题】3. Kafka Streams 和 KSQL 是什么?它们在什么场景下使用? ​

【标准答案】

Kafka Streams 是 Kafka 官方提供的流处理库(不是框架,是 Library)。

核心特点:

  • 嵌入在 Java 应用中,无需独立集群
  • 提供窗口、聚合、Join、状态存储等算子
  • 利用 Kafka 的 Topic 作为状态后端(State Store)

典型使用场景:

java
KStream<String, String> stream = builder.stream("input-topic");
stream
    .filter((key, value) -> value.contains("error"))
    .mapValues(value -> value.toUpperCase())
    .to("output-topic");
  • 实时 ETL(数据清洗、转换)
  • 流式聚合(如:每分钟订单量)
  • 流表 Join(KStream 与 KTable Join)

KSQL(现名 ksqlDB):

  • 用 SQL 语法写流处理,不需要写 Java 代码
  • 底层基于 Kafka Streams
  • 支持 CREATE STREAM, CREATE TABLE, SELECT ... FROM STREAM
sql
CREATE STREAM page_views AS
SELECT user_id, COUNT(*) AS view_count
FROM page_views_raw
WINDOW TUMBLING (SIZE 1 MINUTE)
GROUP BY user_id;

Kafka Streams vs KSQL 选型:

  • 复杂业务逻辑、需要自定义算子 → Kafka Streams
  • 数据分析师使用、快速原型 → KSQL
  • 生产环境性能要求高 → Kafka Streams(更可控)

面试加分点:

  • Kafka Streams 的 State Store 底层用 RocksDB,状态存储在本地磁盘
  • Changelog Topic 用于状态恢复(节点挂了从 Topic 恢复状态)

【面试题】4. RocketMQ 的消息重试机制是怎样的?重试次数、间隔、死信队列如何配置? ​

【标准答案】

RocketMQ 有两套重试机制:并发消费重试 + 顺序消费重试。

并发消费重试(Consumer Concurrently):

重试流程:

  • 消费失败返回 RECONSUME_LATER
  • Broker 将消息写入 %RETRY%ConsumerGroup Topic
  • 延迟一段时间后重新投递给消费者

重试次数与间隔:

第1次重试:延迟 10s
第2次重试:延迟 30s
第3次重试:延迟 1min
第4次重试:延迟 2min
第5次重试:延迟 3min
第6次重试:延迟 4min
第7次重试:延迟 5min
第8~16次重试:每次延迟 10min
  • 默认最多重试 16 次
  • 超过 16 次 → 进入死信队列 %DLQ%ConsumerGroup

顺序消费重试(Consumer Orderly):

  • 消费失败 → 暂停该队列的消费,一直重试
  • 不会跳过,保证顺序性

配置方式:

java
// 生产者:设置消息重试次数(发送失败时)
producer.setRetryTimesWhenSendFailed(3);

// 消费者:设置最大重试次数
consumer.setMaxReconsumeTimes(10);

// Broker 配置:死信队列自动创建
broker.conf:
messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h

死信队列处理:

  • 死信队列是普通 Topic,可以正常消费
  • 人工介入:排查失败原因,补数据或修正逻辑后重新消费

面试加分点:

  • 重试次数不是无限配置,最大 16 次(硬编码)
  • 重试 Topic 的消息会带 RECONSUME_TIMES 属性,消费者可据此判断

【面试题】5. 消息队列的安全机制怎么设计?认证、授权、ACL、TLS 加密如何实现? ​

【标准答案】

消息队列安全四层:认证 → 授权 → 加密 → 审计

第一层:认证(Authentication)

Kafka:

  • SASL 认证:SASL/PLAIN、SASL/SCRAM、SASL/GSSAPI(Kerberos)
  • 配置 listeners=SASL_PLAINTEXT://:9092

RabbitMQ:

  • 内置用户体系(username/password)
  • 支持 LDAP 集成、OAuth2

RocketMQ:

  • ACL 机制(Access Control List)
  • 配置 aclEnable=true,用户名密码在 plain_acl.yml

第二层:授权(Authorization)

Kafka:

  • ACL 规则:Principal + Operation + Resource
bin/kafka-acls --add --allow-principal User:alice \
  --operation Read --topic order-topic
  • 支持 Topic 级别、Group 级别授权

RabbitMQ:

  • vhost 隔离:不同业务用不同 vhost
  • permission:configure、write、read 三种权限

第三层:加密(Encryption)

TLS/SSL 加密传输:

  • Kafka:listeners=SSL://:9093,配置 keystore/truststore
  • RabbitMQ:启用 rabbitmq_auth_mechanism_ssl 插件

第四层:审计(Audit)

  • 记录谁在什么时间访问了哪个 Topic
  • Kafka 可通过 Authorizer 插件实现审计日志

生产建议:

内网环境:SASL_PLAINTEXT(认证即可,不加密,性能最优)
外网/跨机房:SASL_SSL(认证 + 加密)
金融场景:双向 SSL(服务端 + 客户端都验证证书)

面试加分点:

  • Kafka 的 ACL 是存储在 ZooKeeper(旧版)或 KRaft(新版)中的
  • 开启 SSL 后性能下降约 20-30%,需评估

【面试题】6. Kafka 的 Schema Registry 是做什么的?为什么需要消息 Schema 演进? ​

【标准答案】

Schema Registry 是 Confluent 提供的消息 Schema 管理中间件,独立于 Kafka,但通常配套使用。

核心作用:

  • 管理消息的序列化格式(Avro、Protobuf、JSON Schema)
  • 保证生产者和消费者使用兼容的 Schema
  • 支持 Schema 版本管理和演进

为什么需要 Schema 演进?

问题场景:

  • 生产者升级,加了新字段 phone
  • 老消费者不知道 phone 字段,反序列化失败
  • 没有 Schema 管理 → 上下游耦合,升级要停机

Schema 兼容性规则:

  • Backward(向后兼容):新 Schema 能读旧数据(旧消费者能读新消息)
  • Forward(向前兼容):旧 Schema 能读新数据(新消费者能读旧消息)
  • Full(双向兼容):同时满足 Forward + Backward
  • None(不兼容):允许破坏兼容,需上下游同时升级

典型使用:

java
// 生产者用 Avro + Schema Registry
props.put("key.serializer", StringSerializer.class);
props.put("value.serializer", KafkaAvroSerializer.class);
props.put("schema.registry.url", "http://schema-registry:8081");

Schema Registry 高可用:

  • Schema Registry 本身是无状态的,多个实例注册到同一 Kafka Topic(_schemas)
  • 选举一个 Leader 处理写请求

面试加分点:

  • 不用 Schema Registry,用 JSON 也可以,但 JSON 没有类型约束,容易出问题
  • Avro 比 JSON 小 20-30%,序列化更快

【面试题】7. RabbitMQ 集群、Federation、Shovel 三种跨节点方案有什么区别?分别适用什么场景? ​

【标准答案】

RabbitMQ 有三种跨节点/跨机房消息同步方案,设计目标不同。

方案一:Cluster(集群)

原理:

  • 所有节点共享元数据(Exchange、Queue 定义)
  • 消息只存在创建 Queue 的那个节点上(非全复制)
  • 节点间通过 Erlang Cookie 通信

特点:

  • 元数据同步,消息不同步
  • 某个节点挂了,该节点上的 Queue 不可用(消息丢失风险)
  • 适合:同机房、高可用、扩容消费者

方案二:Federation(联邦)

原理:

  • 在不同 Broker(可以是不同集群、不同机房)之间建立 Exchange 级别的同步
  • 上游 Exchange 的消息自动转发到下游 Exchange

特点:

  • 松耦合,双方不需要在同一个集群
  • 支持跨互联网、跨广域网
  • 消息在本地消费失败后,不会阻塞上游

适用场景:

  • 多机房部署,北京机房 → 上海机房同步订单消息
  • 不同业务域的消息互通

方案三:Shovel(铲子)

原理:

  • 在 Broker A 上配置 Shovel,主动从 Broker B 的 Queue 拉取消息,转发到本地 Queue

特点:

  • Queue 级别同步(Federation 是 Exchange 级别)
  • 支持 AMQP 0-9-1 和 AMQP 1.0
  • 配置在源端或目的端均可

适用场景:

  • 需要把老系统的消息"铲"到新系统
  • 跨版本 RabbitMQ 迁移(0.8 → 3.x)

对比总结:

方案同步级别跨机房适用场景
Cluster元数据否同机房高可用
FederationExchange是多机房消息同步
ShovelQueue是消息迁移、跨版本

面试加分点:

  • Cluster 模式 + 镜像队列(Mirrored Queue)才能实现消息高可用
  • 新版本推荐用 Quorum Queue 替代镜像队列

【面试题】8. 如何用消息队列实现微服务之间的 Saga 分布式事务模式? ​

【标准答案】

Saga 模式:把一个分布式事务拆成多个本地事务,通过消息队列协调。

两种实现方式:

方式一:编排式(Choreography,基于事件)

订单服务 → 创建订单(本地事务)→ 发事件 "订单创建"
  ↓
库存服务 → 收到事件 → 扣库存(本地事务)→ 发事件 "库存扣减成功"
  ↓
支付服务 → 收到事件 → 扣余额(本地事务)→ 发事件 "支付成功"
  ↓
订单服务 → 收到事件 → 更新订单状态 = PAID

失败补偿(回滚):

  • 任何一步失败 → 发"失败事件"
  • 前面所有服务监听失败事件,执行补偿事务(反向操作)

方式二:指挥式(Orchestration,基于命令)

Saga 协调器(Orchestrator)
  → 命令:创建订单 → 订单服务
  → 命令:扣库存 → 库存服务
  → 命令:扣余额 → 支付服务
  
任何一步失败 → 协调器发补偿命令

RocketMQ 事务消息实现 Saga:

java
// 订单服务:发送半消息
TransactionSendResult result = producer.sendMessageInTransaction(
    new Message("saga-order", "", "order-123", orderBytes), null);

// 本地事务:创建订单(状态 = PENDING)
// 成功后:Commit 消息 → 库存服务收到,扣库存

// 库存服务消费成功后,发消息给支付服务...
// 任何一步失败 → 发补偿消息(取消订单、返还库存)

关键设计:

  • 每个本地事务必须有对应的补偿事务
  • 补偿事务必须幂等(可能被执行多次)
  • Saga 日志:记录每个步骤的执行状态,方便故障恢复

面试加分点:

  • Saga 不保证隔离性(A 读到 B 的中间状态),需要业务层处理
  • 比 2PC 性能好,适合长事务(如:下单 → 发货 → 确认收货)

【面试题】9. Kafka Connect 的架构是怎样的?Source Connector 和 Sink Connector 分别怎么用? ​

【标准答案】

Kafka Connect 是 Kafka 官方提供的数据集成框架,用于在 Kafka 和其他系统之间流式传输数据。

核心架构:

[外部系统 A] → Source Connector → [Kafka Connect Worker] → Kafka Topic
[Kafka Topic] → [Kafka Connect Worker] → Sink Connector → [外部系统 B]
  • Connector:逻辑单元,定义数据从哪里来、到哪里去
  • Task:实际执行数据拷贝的线程,一个 Connector 可以拆成多个 Task 并行
  • Worker:运行 Connector 和 Task 的进程(Standalone 或 Distributed 模式)

Source Connector(数据源):

  • 把外部系统的数据导入 Kafka
  • 常见:MySQL Binlog → Debezium Connector、JDBC Source、File Source

Sink Connector(数据汇):

  • 把 Kafka 的数据导出到外部系统
  • 常见:JDBC Sink(写入 MySQL)、Elasticsearch Sink、HDFS Sink

使用示例(MySQL → Kafka → Elasticsearch):

bash
# 1. 部署 Source Connector(MySQL Binlog → Kafka)
curl -X POST http://connect:8083/connectors \
  -H "Content-Type: application/json" \
  -d '{
    "name": "mysql-source",
    "config": {
      "connector.class": "io.debezium.connector.mysql.MySqlConnector",
      "database.hostname": "mysql",
      "database.server.id": "184054",
      "table.whitelist": "orders",
      "database.history.kafka.bootstrap.servers": "kafka:9092"
    }
  }'

# 2. 部署 Sink Connector(Kafka → Elasticsearch)
curl -X POST http://connect:8083/connectors \
  -H "Content-Type: application/json" \
  -d '{
    "name": "es-sink",
    "config": {
      "connector.class": "io.confluent.connect.elasticsearch.ElasticsearchSinkConnector",
      "topics": "mysql.orders",
      "connection.url": "http://elasticsearch:9200"
    }
  }'

Distributed 模式 vs Standalone 模式:

  • Standalone:单机运行,适合开发测试
  • Distributed:多 Worker 集群,故障自动转移,生产推荐

面试加分点:

  • Connect 的 offset 管理是自动的,存储在 Kafka 的 connect-offsets Topic
  • Task 失败会自动重启,不丢数据

【面试题】10. Pulsar 的分层存储(Tiered Storage)和分段流(Segmented Stream)是什么?和 Kafka 的存储模型比有什么优势? ​

【标准答案】

Apache Pulsar 是下一代消息中间件,存储模型与 Kafka 有本质区别。

Kafka 的存储模型(回顾):

  • 每个 Partition 是一个物理文件目录
  • 消息顺序写入,分段(Segment)存储
  • Broker 同时负责计算 + 存储,扩容时需要分区迁移

Pulsar 的存储模型:

1. 计算存储分离

  • Broker:无状态,只负责消息读写(计算层)
  • BookKeeper:分布式日志存储(存储层)
  • 扩容 Broker → 直接加 Broker,不需要迁移数据

2. 分段流(Segmented Stream)

  • Topic 不是固定分区,而是分片(Ledger)
  • 每个 Ledger 是一段消息,写满后关闭,新建 Ledger
  • Ledger 均匀分布在 BookKeeper 节点上

3. 分层存储(Tiered Storage)

  • 热数据:存在 BookKeeper(高速存储,SSD)
  • 冷数据:自动迁移到廉价存储(S3、GCS、HDFS)
  • 对消费者透明,读冷数据自动从对象存储拉取

相比 Kafka 的优势:

维度KafkaPulsar
扩容需要分区迁移,耗时长Broker 无状态,秒级扩容
存储成本全部存本地磁盘,成本高冷数据存 S3,成本低 80%
跨地域复制MirrorMaker,配置复杂原生支持,配置简单
消费模型只有 Stream(分区)Stream + Queue(独家)

Pulsar 的消费模型(独家):

  • Exclusive:独占消费(类似 Kafka)
  • Shared:多个消费者共享一个 Subscription(类似 RabbitMQ 的 Queue)
  • Failover:主备消费
  • Key_Shared:按 Key 分片消费(有序 + 并发)

面试加分点:

  • Pulsar 的存储分离设计借鉴了 HDFS + YARN 的思想
  • Kafka 2.8+ 也在做 KRaft(去 ZooKeeper),但存储仍未分离

本套面试题覆盖:Log Compaction、RabbitMQ 流量控制、Kafka Streams/KSQL、RocketMQ 重试机制、消息队列安全、Schema Registry、RabbitMQ 跨节点方案、Saga 分布式事务、Kafka Connect、Pulsar 存储模型。