Kafka 架构深度解析
LinkedIn 日均处理万亿级消息,却能保持毫秒级延迟。支撑这个规模的,正是 Kafka 独特的架构设计。理解 Kafka 的架构,是掌握现代分布式消息队列的必修课。
Kafka 整体架构
Kafka 是一个分布式流平台,采用分区副本和顺序写的设计,实现了高吞吐、高可靠和水平扩展的统一。
flowchart TB
subgraph Cluster["Kafka 集群"]
subgraph Broker1["Broker 1"]
P1["Partition 0 (Leader)"]
P3["Partition 2 (Follower)"]
end
subgraph Broker2["Broker 2"]
P2["Partition 0 (Follower)"]
P4["Partition 2 (Leader)"]
end
subgraph Broker3["Broker 3"]
P5["Partition 1 (Leader)"]
P6["Partition 1 (Follower)"]
end
end
Producer --> |分区 Leader| P1
Producer --> P5
Consumer1 --> P1
Consumer2 --> P5
核心组件
Broker:Kafka 服务实例,一个 Kafka 集群由多个 Broker 组成。每个 Broker 存储一部分分区,Broker 之间相互独立。
Producer:消息生产者,将消息发送到指定 Topic 的分区。
Consumer:消息消费者,从分区读取消息并处理。
Topic:消息的逻辑分类容器,每个 Topic 包含若干分区。
Partition:分区的物理存储单元,每个分区可以有多个副本,分布在不同 Broker 上。
Topic 与分区分布
一个 Topic 可以有多个分区,分区分布在不同的 Broker 上,实现数据分片和并行处理。
Topic: orders (6 partitions, replication-factor: 3)
Broker-1: Partition-0 (Leader), Partition-3 (Follower), Partition-5 (Follower)
Broker-2: Partition-1 (Follower), Partition-4 (Leader), Partition-0 (Follower)
Broker-3: Partition-2 (Leader), Partition-5 (Leader), Partition-1 (Follower)
分区副本机制
每个分区有多个副本(由 replication-factor 配置),其中一个是 Leader 副本,负责处理读写请求;其他是 Follower 副本,异步从 Leader 同步数据。
sequenceDiagram
Producer->>Leader: 发送消息
Leader->>Leader: 写入本地日志
Leader->>Follower1: 同步消息
Leader->>Follower2: 同步消息
Leader-->>Producer: 返回确认
Follower1-->>Leader: 同步完成
Follower2-->>Leader: 同步完成
Leader 故障时,Kafka Controller 会从 ISR(In-Sync Replicas,同步副本集合)中选举新的 Leader。ISR 是与 Leader 保持同步的副本集合,只有在 ISR 中的副本才有资格被选为 Leader。
日志存储结构
Kafka 的消息存储在分区的日志文件中,每个分区对应一个目录,目录下包含多个日志段(Segment)。
kafka-logs/
└── orders-0/
├── 00000000000000000000.log # 日志数据文件
├── 00000000000000000000.index # 偏移量索引文件
├── 00000000000000000000.timeindex # 时间戳索引文件
├── 00000000000000012345.log # 新的日志段
├── 00000000000000012345.index
└── leader-epoch-checkpoint # Leader Epoch 文件
日志段(Segment)
每个日志段包含一个数据文件(.log)和两个索引文件(.index 和 .timeindex):
.log:存储实际消息,追加写入
.index:偏移量索引,按 offset 定位消息位置
.timeindex:时间戳索引,按时间戳查找消息
日志清理策略
Kafka 支持两种日志清理策略:
删除策略(Delete):按时间或大小删除旧日志段
log.retention.hours=168 # 保留 7 天
log.retention.bytes=-1 # 不限制大小(按时间清理)
log.cleanup.policy=delete # 删除策略
压缩策略(Compact):对相同 Key 的消息保留最新一条
log.cleanup.policy=compact # 压缩策略
log.cleaner.min.compaction.lag.ms=3600000 # 消息至少保留 1 小时
压缩策略适用于需要保留最新状态的消息,如数据库变更日志(Changelog)。
高性能原理
Kafka 能够实现百万级 QPS,核心在于三个技术:顺序写、零拷贝、页缓存。
顺序写
消息追加写入日志文件末尾,利用磁盘顺序写的特性,吞吐量可以媲美内存写入。
# 顺序写:写入指针始终在末尾
[msg1][msg2][msg3][msg4][msg5]...
↑
写入指针
随机写的吞吐量约为 100200 MB/s,而顺序写可以达到 500600 MB/s,差距可达数倍。
零拷贝
传统数据读取需要 4 次拷贝、4 次上下文切换:
磁盘 → 内核缓冲区 → 用户缓冲区 → Socket 缓冲区 → 网卡
Kafka 使用 Linux 的 sendfile 系统调用,实现零拷贝:
磁盘 → 内核缓冲区 → 网卡
(直接传递,避免用户态)
减少内存拷贝和上下文切换,可将性能提升 2~3 倍。
页缓存(Page Cache)
操作系统将磁盘数据缓存到内存中。写入数据先写入页缓存,后台异步刷盘;读取数据时优先从页缓存读取。
写入路径: 应用 → 页缓存 → 磁盘(异步)
读取路径: 应用 ← 页缓存 ← 磁盘(缓存命中)
Kafka 的顺序读写模式完美匹配页缓存的工作方式,大部分读取都发生在缓存中,极大提升了性能。
配置建议:如果 Kafka 与其他应用共享机器,页缓存可能被挤占。建议 Kafka 独占机器或使用容器隔离,确保页缓存稳定。
Controller 与集群管理
Kafka 集群中有一个 Broker 被选举为 Controller,负责管理整个集群的元数据和副本状态。
Controller 的职责:
- 监听 Broker 上下线事件
- 为分区选举新的 Leader
- 管理分区副本的分配和状态
- 处理 Topic 创建、删除等元数据变更
flowchart LR
BrokerFailure["Broker 宕机"] --> Controller["Controller 检测"]
Controller --> |Partition-0 Leader 不可用| ISR["从 ISR 选举新 Leader"]
ISR --> Replica1["Replica-B 成为新 Leader"]
Replica1 --> Metadata["更新集群元数据"]
Metadata --> Consumer["Consumer 感知变更"]
Controller 使用 Zookeeper(早期)或 KRaft(新版)实现选举和高可用,确保集群管理的可靠性。
生产配置建议
# Broker 配置
num.network.threads=8 # 网络线程数
num.io.threads=16 # I/O 线程数
socket.send.buffer.bytes=102400 # Socket 发送缓冲区
socket.receive.buffer.bytes=102400
num.partitions=12 # 默认分区数(可根据吞吐量调整)
default.replication.factor=3 # 默认副本数
# Topic 配置
min.insync.replicas=2 # ISR 最小副本数
retention.ms=604800000 # 保留 7 天
理解 Kafka 架构,不是为了记住这些组件叫什么,而是理解为什么这样设计。顺序写和零拷贝是对磁盘和网络特性的极致利用;分区和副本是对可靠性与性能的权衡;Controller 是分布式协调的简化实现。这些设计思想,在其他分布式系统中同样可以看到。