vault backup: 2026-05-24 20:51:06

This commit is contained in:
hhs
2026-05-24 20:51:06 +08:00
parent 910d16682b
commit 686c0a5bd5
52 changed files with 9246 additions and 0 deletions
+223
View File
@@ -0,0 +1,223 @@
---
tags: [MQ, Kafka, 消息队列, 分布式系统]
create time: 2026-05-24 19:52
---
# Kafka
## 概述
Kafka 是 LinkedIn 开源的分布式流处理平台,以极高的吞吐量和水平扩展能力著称。它采用分区日志(Partition Log)作为核心存储模型,配合副本机制和消费者组协议,成为大数据和事件驱动架构的事实标准。
## 正文
### 1. 架构总览
Kafka 集群由多个 Broker 组成,消息按 Topic 逻辑分类,每个 Topic 被划分为若干 Partition 分散在不同 Broker 上。每个 Partition 有多个 Replica(副本),其中一个为 Leader 负责读写,其余 Follower 通过拉取同步数据。
早期版本依赖 ZooKeeper 管理元数据(Broker 注册、Topic 配置、Leader 选举等),从 Kafka 3.3 开始正式推出 KRaft 模式,用内置的 Raft 共识协议取代 ZooKeeper,简化了运维和启动流程。
```mermaid
graph TB
subgraph Cluster["Kafka 集群"]
B1["Broker 1<br/>Leader P0, Follower P1"]
B2["Broker 2<br/>Leader P1, Follower P2"]
B3["Broker 3<br/>Leader P2, Follower P0"]
end
Producer["Producer"] --> B1
Producer --> B2
Producer --> B3
B1 --> ConsumerGroup["Consumer Group"]
B2 --> ConsumerGroup
B3 --> ConsumerGroup
B1 <-.-> B2
B2 <-.-> B3
B3 <-.-> B1
Controller["KRaft Controller<br/>元数据管理"] --> B1
Controller --> B2
Controller --> B3
style Cluster fill:#f9f9f9,color:#333
style Controller fill:#4A90D9,color:#fff
```
### 2. 高吞吐原理
Kafka 单集群可轻松达到百万级 TPS,核心秘诀在于:
- **顺序写磁盘**:Partition 是 append-only 日志,写入永远追加到文件末尾,避免了随机 I/O。顺序写磁盘的吞吐甚至可以媲美内存随机写(600MB/s vs 200MB/s 这种量级)。
- **零拷贝(sendfile)**:Consumer 拉取消息时,Kafka 直接通过 Linux `sendfile` 系统调用将 Page Cache 中的数据发送到网卡,绕过用户态缓冲区,减少两次上下文切换和数据拷贝。
- **批量发送**:Producer 端将多条消息攒成一个批次(batch)再发送,减少网络 RTT。`linger.ms` 和 `batch.size` 控制攒批策略。
- **消息压缩**:支持 Snappy、LZ4、Zstd 等压缩算法,在 Producer 端压缩、Consumer 端解压,显著减少网络带宽和磁盘占用。
- **分区并行**:多个 Partition 可以被不同的 Consumer 并行消费,也可以被不同的 Producer 并行写入,天然支持水平扩展。
> [!question] 思考:零拷贝要求数据不能被修改,那 Kafka 对消息做压缩怎么办?
> 压缩发生在 Producer 端,写入 Partition 的数据已经是压缩后的字节流。Broker 存储和传输时保持原样,解压由 Consumer 端完成。所以零拷贝和压缩并不冲突——Broker 不需要解压就能直接 sendfile。
### 3. 副本机制
Kafka 的数据可靠性靠副本机制保证。每个 Partition 有多个 Replica,分布在不同 Broker 上:
- **Leader**:处理所有读写请求。
- **Follower**:被动拉取 Leader 的数据,保持同步。
- **ISR(In-Sync Replicas)**:与 Leader 保持同步的副本集合。如果 Follower 落后太多(由 `replica.lag.time.max.ms` 控制),会被踢出 ISR。
- **LEO(Log End Offset)**:每个副本最后一条消息的下一个偏移量。
- **HW(High Watermark)**:ISR 中最小的 LEO。Consumer 只能读到 HW 之前的消息,保证读到的数据在所有 ISR 副本上都存在。
Leader 崩溃时,Controller 从 ISR 中选举新 Leader。如果 `unclean.leader.election.enable=true`,允许从非 ISR 副本中选举(可能丢数据),生产环境应设为 `false`。
> [!question] 为什么 HW 要取 ISR 中最小的 LEO,而不是最大或平均值?
> 因为 HW 的意义是"所有同步副本都确认收到的位置"。取最小值才能保证:即使 Leader 挂了,任何一个 ISR 副本接替后,Consumer 已读到的数据都不会丢失。
### 4. Producer 分区策略
Producer 发送消息时需要决定发往哪个 Partition:
| 策略 | 说明 |
|------|------|
| **轮询(Round-Robin)** | 默认策略,消息均匀分配到各 Partition,保证最大吞吐 |
| **Key 哈希** | 指定 Key 时,对 Key 做 hash 取模确定 Partition。相同 Key 的消息总是发到同一 Partition,保证局部有序 |
| **自定义** | 实现 `Partitioner` 接口,按业务逻辑路由(如按用户 ID 分区) |
**acks 参数**控制 Producer 的可靠性级别:
| acks | 含义 | 适用场景 |
|------|------|----------|
| `0` | 不等确认,发完即忘 | 日志采集等允许丢失的场景 |
| `1` | Leader 写入成功即返回 | 大多数业务场景,平衡性能与可靠性 |
| `all` | 所有 ISR 副本写入成功才返回 | 金融级可靠性要求 |
> [!question] 思考题:Kafka 的 acks=all + min.insync.replicas=2 配置为什么能兼顾性能和可靠性?
>
> **提示**:想想如果只有 1 个副本(min.insync.replicas=1),acks=all 等于 acks=1,可靠性没有实质提升。设为 2 意味着至少有两个副本确认,即使 Leader 宕机,数据也不会丢。而 ISR 副本通常是同步延迟极低的(毫秒级),所以对性能影响有限。这个组合的精髓在于:acks=all 保证写入被多个副本确认,min.insync.replicas 保证 ISR 中至少有足够副本,两者配合才真正实现了"不丢数据"。
### 5. Consumer 与 Consumer Group
Consumer Group 是 Kafka 实现"发布-订阅"和"队列"两种语义的核心机制:
- 同一 Group 内的 Consumer 分担消费(类似队列模式)。
- 不同 Group 各自独立消费全部消息(类似发布-订阅模式)。
**Rebalance**:当 Consumer 加入/离开 Group,或 Topic 分区数变化时,触发分区重新分配。Kafka 引入了 **Cooperative Rebalance**(渐进式再平衡),避免 Stop-the-World 式的全局暂停。
**Offset 管理**:
- **自动提交**:`enable.auto.commit=true`,后台定期提交 Offset。简单但可能丢消息(提交后还没处理完就崩溃)或重复消费。
- **手动提交**:消费完成后显式调用 `commitSync()` 或 `commitAsync()`。更可靠,是生产环境推荐做法。Offset 存储在 `__consumer_offsets` 内部 Topic 中。
```go
// 使用 confluent-kafka-go 创建 Producer
package main
import (
"fmt"
"github.com/confluentinc/confluent-kafka-go/v2/kafka"
)
func main() {
// 创建 Producer 实例,指定 Broker 地址
p, err := kafka.NewProducer(&kafka.ConfigMap{
"bootstrap.servers": "localhost:9092",
"acks": "all", // 等待所有 ISR 副本确认
})
if err != nil {
panic(err)
}
defer p.Close()
topic := "my-topic"
// 发送消息
err = p.Produce(&kafka.Message{
TopicPartition: kafka.TopicPartition{
Topic: &topic,
Partition: kafka.PartitionAny, // 自动选择分区
},
Value: []byte("hello kafka"),
}, nil)
if err != nil {
fmt.Printf("produce failed: %v\n", err)
}
// 确保所有消息发送完成
p.Flush(5000)
}
```
```go
// 使用 confluent-kafka-go 创建 Consumer
package main
import (
"fmt"
"github.com/confluentinc/confluent-kafka-go/v2/kafka"
)
func main() {
// 创建 Consumer 实例,加入 Consumer Group
c, err := kafka.NewConsumer(&kafka.ConfigMap{
"bootstrap.servers": "localhost:9092",
"group.id": "my-group",
"auto.offset.reset": "earliest", // 从最早消息开始消费
"enable.auto.commit": false, // 关闭自动提交,手动控制
})
if err != nil {
panic(err)
}
defer c.Close()
// 订阅 Topic
c.SubscribeTopics([]string{"my-topic"}, nil)
for {
// 轮询拉取消息,超时 100ms
msg, err := c.ReadMessage(100e6)
if err != nil {
continue // 超时或错误,继续轮询
}
fmt.Printf("received: %s (partition=%d, offset=%d)\n",
string(msg.Value), msg.TopicPartition.Partition, msg.TopicPartition.Offset)
// 处理完成后手动提交 Offset
if _, err := c.CommitMessage(msg); err != nil {
fmt.Printf("commit failed: %v\n", err)
}
}
}
```
### 6. Kafka 生态
Kafka 不只是一个消息队列,而是一个完整的流处理生态:
| 组件 | 作用 |
|------|------|
| **Kafka Connect** | 数据集成框架,通过 Source/Sink Connector 在 Kafka 与外部系统(MySQL、Elasticsearch、S3 等)之间搬运数据,无需写代码 |
| **Kafka Streams** | 客户端流处理库,直接嵌入应用中,支持窗口、Join、聚合等操作,无需独立的流处理集群 |
| **Schema Registry** | 管理消息 Schema(Avro/Protobuf/JSON Schema),保证生产者和消费者的 Schema 兼容性,防止数据格式不匹配 |
| **ksqlDB** | 用 SQL 语法实时查询和处理 Kafka 流数据,降低流处理门槛 |
### 7. KRaft 模式
Kafka 早期依赖 ZooKeeper 做元数据管理和 Leader 选举,但 ZooKeeper 带来了额外的运维负担(独立集群、脑裂风险、元数据瓶颈)。KRaft(Kafka Raft)模式将元数据管理内置到 Kafka 自身:
- Controller 节点通过 Raft 协议选举,维护元数据日志。
- Broker 从 Controller 拉取元数据,不再依赖 ZooKeeper。
- 启动更快、集群规模上限更高(支持百万级 Partition)、运维更简单。
- Kafka 3.3 起 KRaft 进入生产就绪状态,4.0 版本已完全移除 ZooKeeper 支持。
> [!question] KRaft 用 Raft 管理元数据,那 Kafka 本身的数据副本同步为什么不用 Raft?
> Kafka 的数据副本同步是 Leader-Follower 模型 + ISR 机制,本质是"主从异步复制 + 可配置同步确认"。Raft 更适合小数据量的元数据共识,而 Kafka 的 Partition 日志数据量巨大,ISR 模型通过 HW 机制实现了类似 Raft 的"多数派确认"语义,同时保持了高吞吐的顺序写优势。
## 关联笔记
- [[01-基础概念/1-MQ-基础概念|MQ 基础概念]]
- [[02-消息模型/3-MQ-消息模型|MQ 消息模型]]
- [[04-存储引擎/8-MQ-存储引擎设计|MQ 存储引擎设计]]
- [[05-可靠性保障/12-MQ-消息确认与持久化|MQ 消息确认与持久化]]
- [[05-可靠性保障/15-MQ-顺序性保障|MQ 顺序性保障]]
- [[06-高级特性/21-MQ-消息压缩与批处理|MQ 消息压缩与批处理]]
- [[09-流处理与事件驱动/33-MQ-与流处理|MQ 与流处理]]
- [[12-架构与实战/45-MQ-跨集群复制与容灾|MQ 跨集群复制与容灾]]
- [[07-主流MQ对比/27-MQ-选型对比|MQ 选型对比]]