Appearance
Kafka 入门指南
Kafka 基础概念
核心概念
┌─────────────────────────────────────────────────────────────────┐
│ Kafka 核心架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Producer ──▶ Topic ──▶ Partition ──▶ Consumer Group │
│ │ │
│ ├── Partition 0 │
│ ├── Partition 1 │
│ └── Partition 2 │
│ │
│ 每个 Partition 有多个副本,分布在不同 Broker │
│ │
└─────────────────────────────────────────────────────────────────┘| 概念 | 说明 |
|---|---|
| Producer | 消息生产者 |
| Consumer | 消息消费者 |
| Consumer Group | 消费者组,同一组内消息只被消费一次 |
| Topic | 消息主题 |
| Partition | 分区,实现水平扩展 |
| Broker | Kafka 服务节点 |
| Replica | 副本,保证高可用 |
💡 实战场景:
- 日志收集:Flume → Kafka → Hadoop
- 消息系统:订单通知、事件驱动
- 流处理:实时数据分析
消息可靠性
ACK 机制
💡 实战场景:下单支付后,系统需要确保消息不丢失
bash
# Producer 发送消息的确认级别
acks=0 # 不等待Leader确认,可能丢失消息
acks=1 # Leader 确认收到即可
acks=all # 所有 ISR 副本确认
# ISR (In-Sync Replicas)
// 与 Leader 保持同步的副本集合
min.insync.replicas=2 # ISR 最小数量┌─────────────────────────────────────────────────────────────────┐
│ 消息可靠性等级 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ acks=0 ⭐☆☆☆☆ 最快,可能丢失 │
│ acks=1 ⭐⭐⭐☆☆ 平衡,默认选择 │
│ acks=all ⭐⭐⭐⭐⭐ 最安全,性能最低 │
│ │
└─────────────────────────────────────────────────────────────────┘面试点:⭐⭐⭐⭐⭐ 实战重要性:⭐⭐⭐⭐⭐
消费者组
消费者组机制
┌─────────────────────────────────────────────────────────────────┐
│ 消费者组分区分配 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 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 │
│ │
└─────────────────────────────────────────────────────────────────┘java
// 消费者组
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Arrays.asList("my-topic"));
// 同一组内分区分配
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
for (ConsumerRecord<String, String> record : records) {
System.out.println(record.value());
}
}特点:
- 同一组内 Consumer 不能消费同一 Partition
- 不同组可以重复消费同一消息
- Partition 数量决定最大并行度
分区策略
分区分配规则
java
// 1. 指定 partition
producer.send(new ProducerRecord<>("topic", partition, key, value));
// 2. 按 key 哈希
producer.send(new ProducerRecord<>("topic", key, value));
// key 相同 → 相同 partition(保证顺序)
// 3. 轮询(无 key)
producer.send(new ProducerRecord<>("topic", value));💡 实战场景:保证同一订单的消息有序
java
// 按订单 ID 哈希,保证同一订单的消息在同一 Partition
producer.send(new ProducerRecord<>("order-events", orderId, orderMessage));消息顺序性
单 Partition 顺序保证
┌─────────────────────────────────────────────────────────────────┐
│ 消息顺序场景 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 订单流程(必须有序): │
│ 1. 创建订单 → 2. 支付 → 3. 发货 → 4. 签收 │
│ │
│ 解决方式: │
│ - 相同订单 ID 发到同一 Partition │
│ - 单 Partition 单 Consumer │
│ │
└─────────────────────────────────────────────────────────────────┘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) {
// 按顺序处理
}
}面试点:⭐⭐⭐⭐ 实战重要性:⭐⭐⭐⭐
消费者位移管理
位移提交
java
// 自动提交(默认)
props.put("enable.auto.commit", "true");
props.put("auto.commit.interval.ms", "5000");
// 手动提交
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 事务 │
│ - 用于支付等关键场景 │
│ │
└─────────────────────────────────────────────────────────────────┘分区副本
副本机制
┌─────────────────────────────────────────────────────────────────┐
│ 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 (Log End Offset):当前分区写入消息的最大 offset
- HW (High Watermark):已同步到所有副本的消息最大 offset
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 核心知识点:
| 知识点 | 面试频率 | 实战重要性 |
|---|---|---|
| 基本概念 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 消息可靠性 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 消费者组 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 分区策略 | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| 消息顺序 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 副本机制 | ⭐⭐⭐⭐ | ⭐⭐⭐ |
⚠️ 易错点提醒:
- 同一 Consumer Group 内消息不重复消费
- 不同 Consumer Group 可以重复消费同一消息
- acks=all 配合 min.insync.replicas=2 保证高可靠
- 消息顺序只在同一 Partition 内保证
- 消费者需要手动提交位移才能保证 exactly-once