kafka各个组件的含义,工作流程

吴书松
吴书松
发布于 2026-10-14 / 13 阅读
0

kafka各个组件的含义,工作流程

一、先看全局架构

text

                    ┌─────────────────────────────────────────────┐
                    │              Kafka Cluster                  │
                    │                                             │
   ┌──────────┐     │  ┌──────────┐  ┌──────────┐  ┌──────────┐  │     ┌──────────┐
   │ Producer │────▶│  │ Broker 1 │  │ Broker 2 │  │ Broker 3 │  │────▶│ Consumer │
   └──────────┘     │  │          │  │          │  │          │  │     └──────────┘
                    │  │ P0(Leader)│  │ P1(Leader)│  │ P2(Leader)│  │     ┌──────────┐
   ┌──────────┐     │  │ P1(Follower)│ │ P2(Follower)│ │ P0(Follower)│ │────▶│ Consumer │
   │ Producer │────▶│  │          │  │          │  │          │  │     └──────────┘
   └──────────┘     │  └──────────┘  └──────────┘  └──────────┘  │
                    │                                             │
                    │        ZooKeeper / KRaft (元数据)            │
                    └─────────────────────────────────────────────┘

核心角色:

角色

中文

职责

Broker

代理节点

一台 Kafka 服务器,存储消息、处理读写请求

Topic

主题

逻辑上的消息分类,类似"频道"

Partition

分区

Topic 的物理分片,真正存消息的地方

Replica

副本

Partition 的备份,保证高可用

Segment

日志段

Partition 下的物理文件单位

Producer

生产者

发送消息到 Broker

Consumer

消费者

从 Broker 拉取消息

Consumer Group

消费者组

一组消费者的集合,决定消费模式

Offset

偏移量

消息在分区中的位置编号

下面逐个拆解,最后串起来。


二、Broker:Kafka 的物理载体

2.1 什么是 Broker

Broker 就是一台运行 Kafka 服务的机器(或容器)。多个 Broker 组成一个 Kafka Cluster(集群)。

每个 Broker 有唯一的 broker.id,在 server.properties 中配置:

properties

broker.id=1
listeners=PLAINTEXT://localhost:9092
log.dirs=/var/kafka-logs

2.2 Broker 的职责

  1. 存储消息:把 Producer 发来的消息写入磁盘。

  2. 提供读写:响应 Producer 的写入和 Consumer 的拉取。

  3. 副本管理:维护 Partition 的 Leader/Follower 关系,参与 ISR 同步。

  4. 元数据管理:向 ZooKeeper(或 KRaft)注册自身状态。

2.3 集群中的角色分工

  • Controller:集群中会选出一个 Broker 作为 Controller,负责分区 Leader 选举、副本分配等管理操作。

  • 普通 Broker:负责数据读写。

  • Leader / Follower:针对每个 Partition 而言,不是针对 Broker。

⚠️ 一个 Broker 可以同时是某些 Partition 的 Leader,又是另一些 Partition 的 Follower。Leader/Follower 是分区的属性,不是 Broker 的属性。


三、Topic 与 Partition:逻辑与物理的分层

3.1 Topic 是逻辑概念

Producer 发送时指定 Topic 名称,Consumer 订阅时也指定 Topic 名称。Topic 本身不存储数据,只是一个逻辑分类。

text

Topic: order-topic
   ├── Partition 0
   ├── Partition 1
   └── Partition 2

3.2 Partition 是物理分片

Partition 才是真正存储消息的地方。每个 Partition 是一个有序、不可变的追加日志(Append-only Log)。

text

Partition 0:
  offset:  0    1    2    3    4    5   →  时间方向
         ┌────┬────┬────┬────┬────┬────┐
         │msg0│msg1│msg2│msg3│msg4│msg5│
         └────┴────┴────┴────┴────┴────┘
         旧 ────────────────────────▶ 新
  • 每条消息在 Partition 内有一个唯一的 offset(从 0 递增)。

  • offset 只在单个 Partition 内唯一,不同 Partition 的 offset 互不相关。

  • 消息写入后不可修改,只能追加。

3.3 为什么分区

  1. 水平扩展:单机存储和吞吐有限,分区可分散到多个 Broker。

  2. 并行读写:不同分区可并行读写,提高吞吐。

  3. 负载均衡:分区可均匀分配到各 Broker。

  4. 顺序保证:分区内有序,是 Kafka 顺序性的最小单位。

3.4 Partition 与 Broker 的映射

假设 3 个 Broker、3 个分区、副本数 2:

text

Broker 1:  P0(Leader)   P1(Follower)  P2(Follower)
Broker 2:  P0(Follower) P1(Leader)    P2(Follower)
Broker 3:  P0(Follower) P1(Follower)  P2(Leader)

每个 Partition 有一个 Leader 和若干 Follower,Leader 和 Follower 分布在不同 Broker 上,保证高可用。


四、Replica、Leader、Follower、ISR:高可用的基石

4.1 Replica(副本)

每个 Partition 可以有多个副本,副本数由 replication.factor 决定。

  • Leader Replica:唯一,负责处理所有读写请求。

  • Follower Replica:多个,只负责同步 Leader 的数据,不处理客户端请求。

4.2 读写规则

  • Producer 只写 Leader。

  • Consumer 只读 Leader(Kafka 2.4+ 支持从 Follower 读,但默认仍是 Leader)。

  • Follower 只做同步,定期向 Leader 发送 fetch 请求拉取最新数据。

4.3 ISR(In-Sync Replicas)

ISR 是与 Leader 保持同步的副本集合(包括 Leader 自己)。

text

Partition 0:
  Leader:   Broker 1  ┐
  Follower: Broker 2  ├── ISR = [1, 2, 3]
  Follower: Broker 3  ┘

如果 Broker 3 落后太多或宕机,会被踢出 ISR:

text

ISR = [1, 2]

4.4 acks 与 ISR 的关系

Producer 的 acks 参数决定写入确认级别:

acks

含义

可靠性

0

不等待确认

最低,可能丢消息

1

Leader 写入即确认

中等,Leader 宕机可能丢

all / -1

ISR 全部写入才确认

最高,推荐生产使用

配合 min.insync.replicas(ISR 最小副本数),可以保证强可靠性。

4.5 Leader 选举

当 Leader 所在 Broker 宕机,Controller 会从 ISR 中选出一个新 Leader。

  • 默认优先选 ISR 中的副本,保证数据不丢。

  • 如果 ISR 为空(极端情况),可配置 unclean.leader.election.enable=true 允许非 ISR 副本当 Leader,但会丢数据。


五、Segment:Partition 下的物理文件

很多人把 Partition 和 Segment 搞混。Partition 是逻辑分片,Segment 是 Partition 下的物理文件分段。

5.1 为什么需要 Segment

如果一个 Partition 只有一个巨大文件:

  • 删除旧消息困难。

  • 文件过大,查找慢。

  • 不利于日志清理和压缩。

所以 Kafka 把 Partition 切分成多个 Segment:

text

Partition 0 (目录: order-topic-0/)
  ├── 00000000000000000000.log      ← Segment 1 (offset 0~999)
  ├── 00000000000000000000.index    ← 索引文件
  ├── 00000000000000001000.log      ← Segment 2 (offset 1000~1999)
  ├── 00000000000000001000.index
  ├── 00000000000000002000.log      ← 当前活跃 Segment
  └── 00000000000000002000.index
  • 文件名是该 Segment 第一条消息的 offset。

  • 只有最后一个 Segment 是"活跃"的,可写入。

  • 每个 Segment 默认 1GB(log.segment.bytes),写满后滚动创建新 Segment。

5.2 索引文件

.index 是稀疏索引,记录 offset 到物理位置的映射,加速查找:

text

offset 0    → 文件位置 0
offset 100  → 文件位置 4096
offset 200  → 文件位置 8192

Consumer 要找 offset=150 的消息:先在 index 中定位到 offset=100 的位置,再从该位置顺序扫描到 150。

5.3 日志清理

Kafka 有两种清理策略:

策略

配置

说明

delete

cleanup.policy=delete

按时间/大小删除旧 Segment(默认)

compact

cleanup.policy=compact

按 key 保留最新值,用于 changelog 场景

注意:Kafka 的删除以 Segment 为单位,不会删除 Segment 中间的消息。

5.4 各概念层级关系

text

Kafka Cluster
  └── Broker (物理机器)
        └── Topic (逻辑分类)
              └── Partition (逻辑分片,分布在不同 Broker)
                    └── Replica (副本,Leader + Follower)
                          └── Segment (物理文件分段)
                                └── Message (单条消息,有 offset)

六、Producer:消息如何被发送

6.1 发送流程

text

Producer
  │
  │ 1. 序列化 key/value
  ▼
Serializer
  │
  │ 2. 分区器决定 Partition
  ▼
Partitioner ── hash(key) % partitions ──▶ 选定 Partition
  │
  │ 3. 写入 RecordAccumulator(按分区缓冲)
  ▼
RecordAccumulator (32MB 默认)
  │
  │ 4. Sender 线程批量拉取
  ▼
NetworkClient
  │
  │ 5. 发送到 Partition 的 Leader Broker
  ▼
Broker (Leader)
  │
  │ 6. 写入 Leader,同步到 ISR Follower
  ▼
返回 ack

6.2 关键组件

组件

作用

Serializer

序列化 key/value

Partitioner

决定消息进哪个分区

RecordAccumulator

按分区缓冲消息,攒批

Sender

后台线程,批量发送

InFlightRequests

已发送未确认的请求

6.3 分区选择逻辑

java

// 无 key:粘性分区(Kafka 2.4+)
if (key == null) {
    // 先集中发一个分区,攒批后切换
}

// 有 key:hash 取模
else {
    partition = murmurHash2(key) % numPartitions;
}

6.4 批量与重试

  • 批量:batch.size(默认 16KB)+ linger.ms(默认 0),攒批提高吞吐。

  • 重试:retries(默认 Integer.MAX_VALUE),失败自动重试。

  • 幂等:enable.idempotence=true 保证重试不产生重复消息。


七、Consumer 与 Consumer Group:消息如何被消费

7.1 Consumer 是拉模式

Consumer 主动向 Broker 发送 fetch 请求拉取消息,不是 Broker 推送。

为什么用拉模式:

  • Consumer 按自身处理能力拉取,避免被压垮。

  • 可批量拉取,提高吞吐。

  • 可自主控制 offset。

7.2 Consumer Group(消费者组)

Consumer Group 由 group.id 标识。groupId 是 Kafka 消费语义的核心。

场景

groupId

效果

竞争消费(一条消息只被一个消费者处理)

相同

组内分区分配,负载均衡

广播(所有消费者都收到)

不同

每个组独立消费全量

7.3 分区分配规则

核心规则:同一个 Consumer Group 内,一个 Partition 只能被一个 Consumer 消费。

text

Topic: order-topic (3 分区)
Group: order-group

场景 1: 1 个 Consumer
  Consumer A ← P0, P1, P2

场景 2: 2 个 Consumer
  Consumer A ← P0, P1
  Consumer B ← P2

场景 3: 3 个 Consumer
  Consumer A ← P0
  Consumer B ← P1
  Consumer C ← P2

场景 4: 4 个 Consumer
  Consumer A ← P0
  Consumer B ← P1
  Consumer C ← P2
  Consumer D ← (空闲)

结论:同组内有效消费者数 ≤ 分区数,多出的消费者会空闲。

7.4 分区分配策略

策略

说明

RangeAssignor

按分区范围分配(默认)

RoundRobinAssignor

轮询分配

StickyAssignor

尽量保持原有分配,减少 Rebalance 抖动

CooperativeStickyAssignor

增量式 Rebalance,减少 Stop-The-World

7.5 Rebalance(再均衡)

触发条件:

  1. 消费者加入组(新实例启动)。

  2. 消费者离开组(宕机或主动退出)。

  3. 订阅的 Topic 分区数变化。

Rebalance 过程:

text

1. 所有消费者停止消费(Stop-The-World)
2. 重新选举 Group Coordinator
3. 按策略重新分配分区
4. 各消费者从新分配的分区继续消费

问题:Rebalance 期间整个组暂停消费,频繁 Rebalance 严重影响吞吐。

优化:

  • 调大 session.timeout.ms 和 max.poll.interval.ms。

  • 使用 CooperativeStickyAssignor。

  • 避免消费者处理时间过长。

  • 静态成员(group.instance.id)减少重启触发的 Rebalance。

7.6 Offset 管理

Offset 是 Consumer 在 Partition 中的消费位置。

text

Partition 0:
  ┌────┬────┬────┬────┬────┬────┐
  │msg0│msg1│msg2│msg3│msg4│msg5│
  └────┴────┴────┴────┴────┴────┘
              ▲
              │
        current offset = 3
        (下一条要消费的是 msg3)

Offset 提交方式:

方式

配置

说明

自动提交

enable-auto-commit=true

后台定期提交,可能重复/丢失

手动提交

enable-auto-commit=false

业务控制,推荐

手动提交模式:

ack-mode

说明

RECORD

每条处理完提交

BATCH

一批处理完提交(默认)

MANUAL

调用 acknowledge() 后,下次 poll 时提交

MANUAL_IMMEDIATE

调用 acknowledge() 立即提交

Offset 存储位置:

  • 旧版本:ZooKeeper

  • 新版本:内部 Topic __consumer_offsets(默认 50 个分区)

7.7 Consumer 拉取流程

text

Consumer
  │
  │ 1. 加入 Group,触发 Rebalance,分配分区
  ▼
Group Coordinator
  │
  │ 2. 返回分配的分区列表
  ▼
Consumer
  │
  │ 3. 向分区 Leader 发送 FetchRequest
  ▼
Broker (Leader)
  │
  │ 4. 返回消息 + 当前 offset
  ▼
Consumer
  │
  │ 5. 处理消息
  ▼
  │ 6. 提交 offset 到 __consumer_offsets
  ▼
Group Coordinator

八、一条消息的完整生命周期

把上面所有概念串起来,看一条消息从生产到消费的全过程。

8.1 场景设定

  • 集群:3 个 Broker(B1、B2、B3)

  • Topic:order-topic,3 个分区,副本数 2

  • 分区分布:

    text

    B1: P0(Leader), P1(Follower)
    B2: P1(Leader), P2(Follower)
    B3: P2(Leader), P0(Follower)
  • 生产者:KafkaTemplate<String, String>

  • 消费者:两个实例,groupId 都是 order-group

8.2 生产阶段

text

1. 业务代码调用:
   kafkaTemplate.send("order-topic", "order-1001", "订单已创建");

2. Producer 序列化 key/value:
   key   = "order-1001"
   value = "订单已创建"

3. Partitioner 计算分区:
   hash("order-1001") % 3 = 1
   → 目标 Partition = P1

4. Producer 查询元数据,找到 P1 的 Leader:
   P1 Leader = B2

5. 消息写入 RecordAccumulator 中 P1 的缓冲区

6. Sender 线程批量发送到 B2

7. B2 写入 P1 Leader 日志:
   P1: offset=0 的消息 = "订单已创建"

8. B2 同步给 P1 的 Follower(B1)

9. ISR 全部确认后,返回 ack 给 Producer

8.3 存储阶段

text

P1 在 B2 上的物理文件:
  /var/kafka-logs/order-topic-1/
    ├── 00000000000000000000.log
    │     └── offset=0: key=order-1001, value=订单已创建
    └── 00000000000000000000.index
          └── 0 → 文件位置 0

8.4 消费阶段

text

1. 两个 Consumer 实例(C1、C2)启动,groupId=order-group

2. 向 Group Coordinator 注册,触发 Rebalance

3. 分区分配:
   C1 ← P0, P1
   C2 ← P2

4. C1 向 P1 的 Leader(B2)发送 FetchRequest:
   fetch(partition=P1, offset=0)

5. B2 返回消息:
   offset=0, key=order-1001, value=订单已创建

6. C1 处理消息,业务逻辑执行成功

7. C1 提交 offset=1 到 __consumer_offsets:
   group=order-group, topic=order-topic,
   partition=P1, offset=1

8. 下一条消息到来时,C1 从 offset=1 继续拉取

8.5 完整链路图

text

┌──────────┐
│ 业务代码  │
└────┬─────┘
     │ send(topic, key, value)
     ▼
┌──────────────┐
│  Serializer  │
└────┬─────────┘
     │ bytes
     ▼
┌──────────────┐
│  Partitioner │  hash(key) % 3 = 1
└────┬─────────┘
     │ partition=1
     ▼
┌──────────────────┐
│ RecordAccumulator │  按分区缓冲
└────┬─────────────┘
     │ 批量
     ▼
┌──────────────┐
│ Sender Thread │
└────┬─────────┘
     │ 发到 P1 Leader (B2)
     ▼
┌──────────────────────────────────┐
│ Broker 2                          │
│  ┌────────────────────────────┐  │
│  │ P1 (Leader)                │  │
│  │  offset=0: 订单已创建       │  │
│  └────────────┬───────────────┘  │
│               │ 同步              │
│  ┌────────────▼───────────────┐  │
│  │ P1 (Follower on B1)        │  │
│  └────────────────────────────┘  │
└──────────────────────────────────┘
     ▲
     │ FetchRequest
     │
┌────┴─────┐
│ Consumer │  group=order-group
│    C1    │  分配 P0, P1
└────┬─────┘
     │ 处理成功
     ▼
┌──────────────────────┐
│ __consumer_offsets   │
│  order-group / P1=1  │
└──────────────────────┘

九、各组件协同关系总结

9.1 关系图

text

                    ┌─────────────┐
                    │   Producer  │
                    └──────┬──────┘
                           │ 发送
                           ▼
                    ┌─────────────┐
                    │    Topic    │  逻辑分类
                    └──────┬──────┘
                           │ 分片
              ┌────────────┼────────────┐
              ▼            ▼            ▼
        ┌──────────┐ ┌──────────┐ ┌──────────┐
        │Partition0│ │Partition1│ │Partition2│  逻辑分片
        └────┬─────┘ └────┬─────┘ └────┬─────┘
             │            │            │
             │ 副本       │ 副本       │ 副本
             ▼            ▼            ▼
        ┌──────────┐ ┌──────────┐ ┌──────────┐
        │Leader/   │ │Leader/   │ │Leader/   │  物理副本
        │Follower │ │Follower │ │Follower │
        └────┬─────┘ └────┬─────┘ └────┬─────┘
             │            │            │
             │ 存储在     │ 存储在     │ 存储在
             ▼            ▼            ▼
        ┌──────────┐ ┌──────────┐ ┌──────────┐
        │Segment   │ │Segment   │ │Segment   │  物理文件
        │.log/.index│ │.log/.index│ │.log/.index│
        └──────────┘ └──────────┘ └──────────┘
             ▲            ▲            ▲
             │            │            │
             └────────────┼────────────┘
                          │ 拉取
                    ┌─────┴──────┐
                    │  Consumer  │
                    │  (Group)   │  groupId 决定消费模式
                    └────────────┘

9.2 关键协同规则

关系

规则

Producer → Topic

指定 Topic 名称发送

Producer → Partition

有 key:hash 取模;无 key:粘性分区;可手动指定

Partition → Broker

每个分区分布在不同 Broker 上

Partition → Replica

一个 Leader + 多个 Follower,分布在不同 Broker

Producer → Replica

只写 Leader

Follower → Leader

定期 fetch 同步,落后太多踢出 ISR

Consumer → Partition

同组内一个分区只能被一个消费者消费

Consumer → Group

groupId 相同竞争,不同广播

Consumer → Offset

提交到 __consumer_offsets

Rebalance

消费者增减或分区变化时触发

Segment → Partition

Partition 由多个 Segment 组成

9.3 常见问题速答

Q1:一个 Partition 能被多个 Consumer 消费吗?

  • 同组内:不能,只能被一个 Consumer 消费。

  • 不同组:可以,每个组各自消费一份。

Q2:分区数能增加吗?

  • 能增加,不能减少。增加后 hash(key) % partitions 可能改变,破坏 key 顺序。

Q3:ISR 是什么?

  • 与 Leader 保持同步的副本集合。acks=all 时,ISR 全部确认才返回。

Q4:Segment 和 Partition 什么关系?

  • Partition 是逻辑分片,Segment 是 Partition 下的物理文件分段。

Q5:Consumer 数量超过分区数会怎样?

  • 多出的 Consumer 空闲,浪费资源。

Q6:Rebalance 会丢消息吗?

  • 不会丢消息,但会暂停消费。未提交的 offset 会被重新消费,可能重复。


十、性能与可靠性设计要点

10.1 生产者侧

参数

作用

建议

acks

确认级别

all

retries

重试次数

Integer.MAX_VALUE

enable.idempotence

幂等

true

batch.size

批量大小

16KB~64KB

linger.ms

攒批等待

5~100ms

compression.type

压缩

lz4 或 snappy

10.2 消费者侧

参数

作用

建议

enable-auto-commit

自动提交

false

max-poll-records

单次拉取条数

按处理能力调整

max.poll.interval.ms

处理超时

按业务耗时调整

session.timeout.ms

会话超时

10~30s

fetch.min.bytes

最小拉取字节

1KB~1MB

isolation.level

隔离级别

read_committed(事务场景)

10.3 Broker 侧

参数

作用

建议

num.partitions

默认分区数

按吞吐规划

default.replication.factor

默认副本数

2~3

min.insync.replicas

最小 ISR

2

log.segment.bytes

Segment 大小

1GB

log.retention.hours

保留时长

按业务

unclean.leader.election.enable

非 ISR 选举

false


十一、一句话总结

Kafka 的内部工作原理可以概括为:

Producer 按 key 的 hash 决定消息进入哪个 Partition,Partition 分布在多个 Broker 上,每个 Partition 有 Leader/Follower 副本保证高可用,消息以 Segment 文件形式持久化;Consumer 以 groupId 标识消费者组,同组内分区独占、竞争消费,不同组广播;Consumer 通过提交 Offset 记录消费进度,Rebalance 在消费者变化时重新分配分区。

理解这条链路,就理解了 Kafka 的核心。所有配置、调优、问题排查,最终都围绕这条链路展开。