Appearance
Kafka 面试题
Kafka 基础
Q1: Kafka 的核心概念?
┌─────────────────────────────────────────────────────────────────┐
│ Kafka 核心架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Producer ──▶ Topic ──▶ Partition ──▶ Consumer Group │
│ │ │
│ ├── Partition 0 │
│ ├── Partition 1 │
│ └── Partition 2 │
│ │
│ 每个 Partition 有多个副本,分布在不同 Broker │
│ │
└─────────────────────────────────────────────────────────────────┘| 概念 | 说明 |
|---|---|
| Producer | 消息生产者,负责发送消息 |
| Consumer | 消息消费者,负责接收消息 |
| Consumer Group | 消费者组,同一组内消息只被消费一次 |
| Topic | 消息主题,用于分类消息 |
| Partition | 分区,实现水平扩展 |
| Broker | Kafka 服务节点 |
| Replica | 副本,保证高可用 |
| Offset | 消息偏移量,记录消费位置 |
Q2: Kafka 的工作流程?
┌─────────────────────────────────────────────────────────────────┐
│ Kafka 消息流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Producer Broker Consumer │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐│
│ │ 发送消息 │──▶ 写入 ──▶│ Leader │──▶ 同步 ──▶│ 消费消息 ││
│ │ 到 Topic │ │ Partition│ │ ││
│ └─────────┘ └─────────┘ └─────────┘│
│ │ │
│ ▼ │
│ ┌─────────┐ │
│ │ Follower│ │
│ │ Partition│ │
│ └─────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘写入流程:
- Producer 发送消息到 Topic
- 根据分区策略发送到对应 Partition
- Leader Partition 接收消息
- Follower Partition 同步消息
- Consumer 订阅 Topic 并消费
Q3: Kafka 的分区策略?
1. 指定 partition
java
producer.send(new ProducerRecord<>("topic", partition, key, value));2. 按 key 哈希
java
producer.send(new ProducerRecord<>("topic", key, value));
// key 相同 → 相同 partition(保证顺序)3. 轮询(无 key)
java
producer.send(new ProducerRecord<>("topic", value));
// 依次写入各个 partition消息可靠性
Q4: 如何保证消息不丢失?
┌─────────────────────────────────────────────────────────────────┐
│ 消息可靠性等级 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ acks=0 ⭐☆☆☆☆ 最快,可能丢失 │
│ acks=1 ⭐⭐⭐☆☆ 平衡,默认选择 │
│ acks=all ⭐⭐⭐⭐⭐ 最安全,性能最低 │
│ │
└─────────────────────────────────────────────────────────────────┘1. Producer 端
java
// 配置
props.put("acks", "all"); // 所有副本确认
props.put("retries", 3); // 重试次数
props.put("enable.idempotence", true); // 开启幂等性2. Broker 端
properties
# 分区副本数 >= 2
min.insync.replicas=2
replication.factor=33. Consumer 端
java
// 手动提交 offset
consumer.commitSync();Q5: Kafka 的 ACK 机制?
bash
# acks=0
# 不等待 Leader 确认
# 最快,可能丢失消息
# 适用于日志收集等可容忍丢失的场景
# acks=1
# Leader 确认收到即可
# 平衡选项
# Leader 宕机可能丢失消息
# acks=all (或 -1)
# 所有 ISR 副本确认
# 最安全
# 性能最差ISR(In-Sync Replicas):
┌─────────────────────────────────────────────────────────────────┐
│ ISR 概念 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ISR = 与 Leader 保持同步的副本集合 │
│ │
│ min.insync.replicas = 2 # ISR 最小数量 │
│ │
│ 如果 ISR 数量 < min.insync.replicas: │
│ - Broker 拒绝写入 │
│ - 抛出 NotEnoughReplicasException │
│ │
└─────────────────────────────────────────────────────────────────┘消息顺序性
Q6: Kafka 如何保证消息顺序?
单 Partition 顺序保证:
java
// 按 key 发送到同一分区,保证同一 key 消息顺序
producer.send(new ProducerRecord<>("order-topic", orderId, message));
// 消费者按偏移量顺序消费
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
for (ConsumerRecord<String, String> record : records) {
// 按顺序处理
}
}顺序性场景:
┌─────────────────────────────────────────────────────────────────┐
│ 消息顺序场景 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 订单流程(必须有序): │
│ 1. 创建订单 → 2. 支付 → 3. 发货 → 4. 签收 │
│ │
│ 解决方式: │
│ - 相同订单 ID 发到同一 Partition │
│ - 单 Partition 单 Consumer │
│ │
└─────────────────────────────────────────────────────────────────┘多 Partition 只能保证单 Partition 内有序
消费者组
Q7: 消费者组机制?
┌─────────────────────────────────────────────────────────────────┐
│ 消费者组分区分配 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Topic: my-topic (3 partitions) │
│ │
│ Consumer Group A: │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │C1 (P0) │ │C2 (P1) │ │C3 (P2) │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ 每个 Consumer 消费一个 Partition │
│ │
│ Consumer Group B: │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │C4 (P0,1)│ │C5 (P1) │ │C4 (P2) │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ 2个 Consumer 消费3个 Partition │
│ │
└─────────────────────────────────────────────────────────────────┘特点:
- 同一组内 Consumer 不能消费同一 Partition
- 不同组可以重复消费同一消息
- Partition 数量决定最大并行度
Q8: 消费者 offset 管理?
自动提交(默认):
java
props.put("enable.auto.commit", "true");
props.put("auto.commit.interval.ms", "5000");手动提交:
java
props.put("enable.auto.commit", "false");
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
for (ConsumerRecord<String, String> record : records) {
process(record);
}
// 手动提交
consumer.commitSync();
}精确一次语义:
┌─────────────────────────────────────────────────────────────────┐
│ 消息传递语义 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ at most once(最多一次) │
│ - 异步提交,可能丢失消息 │
│ - Consumer 先提交 offset,再处理消息 │
│ │
│ at least once(至少一次) │
│ - 同步提交,可能重复消费 │
│ - Consumer 先处理消息,再提交 offset │
│ │
│ exactly once(精确一次) │
│ - 事务机制 │
│ - Producer + Consumer 事务 │
│ - 用于支付等关键场景 │
│ │
└─────────────────────────────────────────────────────────────────┘分区副本
Q9: 分区副本机制?
┌─────────────────────────────────────────────────────────────────┐
│ Partition 副本分布 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Partition 0: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Leader: Broker-1 ISR: [Broker-1, Broker-2] │ │
│ │ Follower: Broker-2 (同步中) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Partition 1: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Leader: Broker-2 ISR: [Broker-2, Broker-3] │ │
│ │ Follower: Broker-3 (同步中) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘LEO 和 HW:
┌─────────────────────────────────────────────────────────────────┐
│ LEO 和 HW │
├─────────────────────────────────────────────────────────────────┤
│ │
│ LEO (Log End Offset): │
│ - 当前分区写入消息的最大 offset │
│ │
│ HW (High Watermark): │
│ - 已同步到所有副本的消息最大 offset │
│ - Consumer 只能看到 HW 之前的消息 │
│ - 保证消息一致性 │
│ │
└─────────────────────────────────────────────────────────────────┘Kafka 实战
Q10: 如何实现延迟队列?
方式1:使用延时插件
java
// kafka-delayed-message 插件
producer.send(new ProducerRecord<>("delayed-topic", message), delay);方式2:外部延迟系统 + 定时任务
java
// 发送消息时记录到 Redis ZSet
redisTemplate.opsForZSet().add("delay:queue",
messageId,
System.currentTimeMillis() + delayMs);
// 定时任务扫描 ZSet
Set<String> readyMessages = redisTemplate.opsForZSet()
.rangeByScore("delay:queue", 0, System.currentTimeMillis());方式3:RabbitMQ 的延迟插件
Q11: 如何实现消息幂等?
Producer 端幂等:
java
props.put("enable.idempotence", true);
// 开启后,Producer 自动实现幂等发送
// 每个消息有一个 PID(Producer ID)+ Sequence Number
// 相同 PID + Sequence Number 的消息会被去重Consumer 端幂等:
java
// 业务去重
@RedisCache(expire = 3600)
public Result processMessage(Message msg) {
// 消息 ID 去重
if (processedMessageIds.contains(msg.getId())) {
return Result.success(); // 已处理,直接返回
}
// 处理业务逻辑
processedMessageIds.add(msg.getId());
return Result.success();
}Q12: Kafka 高并发优化?
Producer 端优化:
java
// 1. 批量发送
props.put("batch.size", 16384); // 批量大小
props.put("linger.ms", 10); // 等待时间
// 2. 压缩
props.put("compression.type", "snappy"); // LZ4 / Snappy / GZIP
// 3. 并发发送
List<Future<RecordMetadata>> futures = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
futures.add(producer.send(new ProducerRecord<>("topic", value)));
}Consumer 端优化:
java
// 1. 提高并行度
consumer.subscribe(Arrays.asList("topic"));
// 分区数 = 最大 Consumer 并行度
// 2. 批量消费
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
List<Message> batch = new ArrayList<>();
for (ConsumerRecord<String, String> record : records) {
batch.add(parse(record));
}
processBatch(batch); // 批量处理
}Kafka vs RabbitMQ
Q13: Kafka 和 RabbitMQ 的区别?
| 对比 | Kafka | RabbitMQ |
|---|---|---|
| 吞吐量 | 极高(10万+/s) | 中等(万级/s) |
| 延迟 | 低 | 低 |
| 消息顺序 | 单 Partition 有序 | 队列内有序 |
| 消息回溯 | 支持 | 不支持 |
| 消息持久化 | 支持 | 支持 |
| 消息路由 | 基础 | 丰富 |
| 消息确认 | ACK 机制 | 多种确认模式 |
| 死信队列 | 支持 | 支持 |
| 延迟队列 | 需插件 | 原生支持 |
| 适用场景 | 日志、大数据 | 业务消息 |
选择建议:
- 日志收集、大数据:Kafka
- 业务消息、复杂路由:RabbitMQ
- 高并发、事件驱动:Kafka
- 事务消息:RocketMQ
Q14: 为什么 Kafka 吞吐量高?
┌─────────────────────────────────────────────────────────────────┐
│ Kafka 高吞吐原因 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. 顺序写入 │
│ - 消息追加到文件末尾 │
│ - 磁盘顺序 IO,顺序读写速度接近内存 │
│ │
│ 2. 零拷贝技术 │
│ - sendfile() 系统调用 │
│ - 数据直接在内核空间传输,不经过用户空间 │
│ │
│ 3. 批量处理 │
│ - 消息批量发送 │
│ - 消息批量压缩 │
│ │
│ 4. 分区并行 │
│ - 多 Partition 并行处理 │
│ │
│ 5. 高效序列化 │
│ - Protobuf / Avro 序列化 │
│ │
└─────────────────────────────────────────────────────────────────┘常见问题
Q15: 如何处理消息积压?
原因:
- Consumer 消费能力不足
- Producer 发送速度过快
- Consumer 故障
解决方案:
java
// 1. 增加 Consumer 并行度
// 提高 Consumer 实例数,但不能超过 Partition 数
// 2. 提高消费并发度
@KafkaListener(topics = "topic", concurrency = "3")
public void consume(String message) {
// 多线程处理
executor.execute(() -> process(message));
}
// 3. 批量消费
consumer.poll(Duration.ofMillis(100));
// 一次处理多条消息
// 4. 临时扩容
// 增加 Consumer 实例数Q16: 如何保证不丢消息不重复消费?
不丢消息:
- Producer:acks=all + 重试
- Broker:replication.factor >= 3
- Consumer:手动提交 offset
不重复消费:
- 业务去重表(基于消息 ID)
- 幂等操作(Redis Set)
- 分布式事务
总结
Kafka 高频面试知识点:
| 知识点 | 面试频率 |
|---|---|
| 核心概念(Topic/Partition/Consumer Group) | ⭐⭐⭐⭐⭐ |
| 消息可靠性(ACK 机制) | ⭐⭐⭐⭐⭐ |
| 消费者组机制 | ⭐⭐⭐⭐ |
| 分区副本机制 | ⭐⭐⭐⭐ |
| 消息顺序性 | ⭐⭐⭐⭐ |
| Offset 管理 | ⭐⭐⭐⭐ |
| 高吞吐原因 | ⭐⭐⭐⭐ |
| 消息积压处理 | ⭐⭐⭐ |