本轮 消息中间件面试题库(带答案)
生成时间:2026-05-21 23:20
【面试题】1. Kafka 的日志压缩(Log Compaction)是什么?适用什么场景?和日志删除策略有什么区别?
【标准答案】
Log Compaction(日志压缩) 是 Kafka 的一种日志保留策略(另一种是 Log Deletion)。
核心原理:
- 以 Key 为维度,只保留每个 Key 的最后一个值(最新状态)
- 后台 Compact 线程扫描日志,删除相同 Key 的旧值
- 日志头(Head)不压缩,只压缩日志尾(Tail)
与 Log Deletion 的区别:
| 维度 | Log Deletion | Log Compaction |
|---|---|---|
| 触发条件 | 时间(retention.ms)或大小(retention.bytes) | Key 重复,保留最新值 |
| 数据保留 | 整段删除 | 按 Key 去重保留最新 |
| 适用场景 | 事件流、日志收集 | 状态存储、KV 快照 |
| 例子 | 用户点击日志 | 用户最新手机号、地址 |
适用场景:
- 数据库变更日志(CDC),只需要最新状态
- KV 存储快照(如 Cache 预热)
- Stream 的 State Store(Kafka Streams 内部使用)
配置方式:
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)"模型,天然公平
生产调优:
# 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)
典型使用场景:
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
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%ConsumerGroupTopic - 延迟一段时间后重新投递给消费者
重试次数与间隔:
第1次重试:延迟 10s
第2次重试:延迟 30s
第3次重试:延迟 1min
第4次重试:延迟 2min
第5次重试:延迟 3min
第6次重试:延迟 4min
第7次重试:延迟 5min
第8~16次重试:每次延迟 10min- 默认最多重试 16 次
- 超过 16 次 → 进入死信队列
%DLQ%ConsumerGroup
顺序消费重试(Consumer Orderly):
- 消费失败 → 暂停该队列的消费,一直重试
- 不会跳过,保证顺序性
配置方式:
// 生产者:设置消息重试次数(发送失败时)
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(不兼容):允许破坏兼容,需上下游同时升级
典型使用:
// 生产者用 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 | 元数据 | 否 | 同机房高可用 |
| Federation | Exchange | 是 | 多机房消息同步 |
| Shovel | Queue | 是 | 消息迁移、跨版本 |
面试加分点:
- Cluster 模式 + 镜像队列(Mirrored Queue)才能实现消息高可用
- 新版本推荐用 Quorum Queue 替代镜像队列
【面试题】8. 如何用消息队列实现微服务之间的 Saga 分布式事务模式?
【标准答案】
Saga 模式:把一个分布式事务拆成多个本地事务,通过消息队列协调。
两种实现方式:
方式一:编排式(Choreography,基于事件)
订单服务 → 创建订单(本地事务)→ 发事件 "订单创建"
↓
库存服务 → 收到事件 → 扣库存(本地事务)→ 发事件 "库存扣减成功"
↓
支付服务 → 收到事件 → 扣余额(本地事务)→ 发事件 "支付成功"
↓
订单服务 → 收到事件 → 更新订单状态 = PAID失败补偿(回滚):
- 任何一步失败 → 发"失败事件"
- 前面所有服务监听失败事件,执行补偿事务(反向操作)
方式二:指挥式(Orchestration,基于命令)
Saga 协调器(Orchestrator)
→ 命令:创建订单 → 订单服务
→ 命令:扣库存 → 库存服务
→ 命令:扣余额 → 支付服务
任何一步失败 → 协调器发补偿命令RocketMQ 事务消息实现 Saga:
// 订单服务:发送半消息
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):
# 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-offsetsTopic - 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 的优势:
| 维度 | Kafka | Pulsar |
|---|---|---|
| 扩容 | 需要分区迁移,耗时长 | 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 存储模型。