Kafka 是如何工作的?从 Broker、Partition、Offset 到 Consumer Group

在微服务系统中,Kafka 经常和 Redis、MySQL 一起出现。

例如一个电商系统,用户支付成功之后,除了需要修改订单状态,还可能需要发送短信、更新销售统计、记录用户行为。

一种直接的实现方式是让支付服务依次调用其他服务:

text 复制代码
Payment Service
      |
      +----> Order Service
      |
      +----> Notification Service
      |
      +----> Statistics Service

这种设计能够工作,但存在一个明显问题。

如果 Notification Service 突然响应很慢,支付服务是否要一直等待?如果统计服务暂时不可用,是否应该让支付接口失败?

这些后续操作并不一定需要在当前支付请求中同步完成。

因此,我们可以换一种方式:支付服务只负责发布"支付成功"这个业务事件,其他服务根据自己的职责处理它。

text 复制代码
Payment Service
      |
      | PaymentSucceeded
      v
     Kafka
      |
      +----> Order Service
      |
      +----> Notification Service
      |
      +----> Statistics Service

这样,支付服务不需要依次等待三个下游服务完成各自的任务。

但是随之而来的问题是:Kafka 如何保存这些消息?三个服务怎么同时消费同一条消息?如果 Notification Service 部署了三个实例,会不会发送三次短信?

理解这些问题,就需要从 Kafka 的内部结构开始。

一、Kafka 到底是什么?

Kafka 通常被称为分布式消息系统或分布式事件流平台。

从微服务的角度,可以先把它理解成一个能够持久化保存消息,并允许不同消费者按照各自进度读取消息的系统。

例如支付服务产生一条业务事件:

json 复制代码
{
  "event_id": "E10001",
  "event_type": "PaymentSucceeded",
  "order_id": 10001,
  "payment_id": "P10001"
}

这条消息表达的是:

订单 10001 对应的支付已经成功。

Payment Service 不需要直接知道有哪些服务会处理这个事件,而是把消息发布到 Kafka。

之后,订单服务可以根据消息修改订单状态,通知服务可以发送支付成功通知,统计服务可以更新销售数据。

这里体现了 Kafka 的一个重要价值:

消息生产者与消费者不需要保持直接的同步调用关系。

生产者负责发布事件,消费者根据自己的处理能力读取事件。

不过,异步并不意味着消息发布后业务就一定完成。

例如 Payment Service 把事件写入 Kafka 成功,只能说明它完成了相应的消息发布步骤,并不代表订单状态已经更新、短信已经发出。

这些操作是不同的业务阶段。

Kafka 和 RPC 的区别

上一篇已经介绍了 RPC。

RPC 通常表示:

text 复制代码
Order Service
      |
      | ReserveInventory
      v
Inventory Service
      |
      v
返回库存预留结果

调用方通常需要根据返回结果继续处理当前业务。

Kafka 则更适合:

text 复制代码
Payment Service
      |
      | 发布 PaymentSucceeded
      v
     Kafka
      |
      v
其他服务后续独立处理

所以两者不是替代关系。

RPC 主要解决服务之间的远程请求与响应,Kafka 则提供异步消息存储、传递和消费能力。

什么时候使用哪一种方式,仍然取决于业务是否需要立即得到结果。


二、先认识 Producer、Broker 和 Topic

先看 Kafka 最基本的一条消息链路:

text 复制代码
Producer
    |
    v
  Kafka
    |
    v
Consumer

这里有三个概念。

1. Producer:消息生产者

Producer 负责把消息发送到 Kafka。

例如:

text 复制代码
Payment Service

在发布支付成功事件时,就是一个 Producer。

它发送的消息可以包含:

text 复制代码
Topic: payment-events

Key: 10001

Value: PaymentSucceeded 事件内容

其中:

  • Topic 表示消息要写入哪个主题。
  • Key 可以用于决定消息进入哪个 Partition。
  • Value 是实际的消息内容。

消息还可以携带 Headers、Timestamp 等信息。

需要注意,Producer 是逻辑角色,不一定是一个独立部署的服务。Payment Service 内部的 Kafka 客户端就可以承担 Producer 的职责。

2. Broker:Kafka 服务器节点

Kafka 通常不是只运行在一台服务器上。

例如:

text 复制代码
Kafka Cluster
      |
      +-- Broker 1
      |
      +-- Broker 2
      |
      +-- Broker 3

每个 Broker 是一个 Kafka 服务器节点,负责存储和提供相应的消息分区数据。

多个 Broker 共同组成 Kafka 集群。

但这里要避免一个误解:

Kafka 不是简单地让三个 Broker 各自保存完全不同的 Topic。

一个 Topic 可以包含多个 Partition,这些 Partition 可以分布在不同的 Broker 上,并且可以根据配置拥有副本。

关于 Broker 副本、Leader 和消息确认机制,我们会在下一篇可靠性专题中详细讨论。

3. Topic:消息的逻辑分类

Topic 可以理解成消息所属的逻辑主题。

例如:

text 复制代码
payment-events
order-events
inventory-events

其中:

text 复制代码
payment-events

可以保存支付相关事件。

text 复制代码
order-events

可以保存订单相关事件。

生产者发布消息时,需要指定目标 Topic。

消费者则订阅自己需要处理的 Topic。

例如:

text 复制代码
Payment Service
      |
      v
payment-events
      |
      +----> Order Service
      |
      +----> Notification Service

但这里还有一个问题。

如果 payment-events 每秒产生几十万条消息,仅仅依靠一个 Topic 名称,Kafka 怎么组织这些数据并支持多个消费者并行处理?

这就需要引出 Kafka 最重要的概念之一:Partition。

三、Partition:为什么一个 Topic 还要分成多个分区?

假设 Kafka 中有一个 Topic:

text 复制代码
payment-events

我们可以把它划分为三个 Partition:

text 复制代码
payment-events
      |
      +-- Partition 0
      |
      +-- Partition 1
      |
      +-- Partition 2

Partition 是 Kafka 存储和消费消息的重要单位。

对于一个 Partition,Kafka 会按照追加写入的方式组织消息记录。

例如:

text 复制代码
Partition 0

Message A
Message B
Message C
Message D

从逻辑上看,它像一条持续追加的消息日志。

不同 Partition 可以分别保存不同消息:

text 复制代码
Topic: payment-events

Partition 0:
[A] [D] [G]

Partition 1:
[B] [E] [H]

Partition 2:
[C] [F] [I]

因此,一个 Topic 不一定只有一条完整的消息序列,而可以包含多条独立的分区日志。

Kafka 为什么要设计 Partition?

主要有两个原因。

第一,让消息能够分布式存储。

一个 Topic 的不同 Partition 可以由不同 Broker 承担存储和服务请求的工作。

例如:

text 复制代码
Broker 1:Partition 0

Broker 2:Partition 1

Broker 3:Partition 2

这是简化示例,实际集群还可能存在副本。

这样,一个 Topic 不需要把所有消息都集中在单个 Broker 上。

第二,提高消费并行度。

假设只有一个 Partition:

text 复制代码
Partition 0

在同一个 Consumer Group 内,这个分区同一时刻通常只分配给一个 Consumer 处理。

如果有三个 Partition,就可以把它们分别交给三个 Consumer。

例如:

text 复制代码
Partition 0 → Consumer A

Partition 1 → Consumer B

Partition 2 → Consumer C

三个消费者就可以并行处理不同分区中的数据。

不过,Partition 数量不是越多越好。

更多分区会增加元数据、文件、连接、复制和 Rebalance 等方面的管理成本。

分区数量应该结合吞吐量、消费并行度和业务顺序要求决定。

消息究竟进入哪个 Partition?

假设 Producer 发送三条消息:

text 复制代码
order_id = 10001

order_id = 10002

order_id = 10003

如果发送消息时指定 order_id 作为 Key,生产者通常可以根据 Key 的分区规则,将相同 Key 的消息发送到同一个 Partition。

例如:

text 复制代码
order_id = 10001 → Partition 1

order_id = 10002 → Partition 0

order_id = 10003 → Partition 2

后续订单 10001 的消息继续使用相同 Key,在分区数量和分区规则保持不变时,通常也会被发送到 Partition 1。

这种设计对维护同一业务对象的消息顺序非常重要。

但如果后续修改了 Topic 的分区数量,Key 到 Partition 的映射就可能发生变化,因此不能把某个 Key 永远对应某个固定分区当成绝对保证。


四、Offset:Kafka 怎么知道消息读到哪里了?

现在我们已经知道,每个 Partition 内部都保存着一条有顺序的消息日志。

但 Consumer 消费这些消息时,还需要知道自己读到哪个位置。

Kafka 使用 Offset(偏移量)标识 Partition 中的消息位置。

例如:

text 复制代码
Partition 0

Offset 0    Message A
Offset 1    Message B
Offset 2    Message C
Offset 3    Message D
Offset 4    Message E

这里的 Offset 可以理解成分区日志中的位置编号。

需要特别注意:

Offset 只在对应的 Partition 内具有意义,并不是整个 Topic 统一共享的消息编号。

例如:

text 复制代码
Partition 0:
Offset 0、1、2、3

Partition 1:
Offset 0、1、2、3

两个 Partition 都可以存在 Offset 0。

因此,准确定位一条 Kafka 消息,需要结合 Topic、Partition 和 Offset。

Consumer 怎么使用 Offset?

假设一个 Consumer 正在消费 Partition 0:

text 复制代码
Offset 0 → 已处理
Offset 1 → 已处理
Offset 2 → 已处理
Offset 3 → 下一条待处理

它可以从 Offset 3 对应的位置继续读取。

这里我们还需要区分两个概念:

Consumer 当前读取到的位置,与它已经提交的 Offset,不一定相同。

Consumer 可以先读取多条消息,然后在处理完成后提交消费进度。

例如:

text 复制代码
读取 Offset 3
      |
      v
处理业务
      |
      v
读取 Offset 4
      |
      v
处理业务
      |
      v
提交 Offset 5

在 Kafka 的常见提交语义中,提交 Offset 5 表示:

此前的相应消息已经按消费者的提交策略处理,下一次应从 Offset 5 开始读取。

这里提交的是下一条将要消费的位置。

但 Kafka 并不会因为 Consumer 提交了 Offset,就立即删除前面的消息。

这一点非常重要。

Kafka 消费完消息以后,为什么不删除?

如果把 Kafka 理解成一种简单队列,可能会认为:

text 复制代码
Consumer 读取消息
        |
        v
消息从 Kafka 删除

但 Kafka 并不是按照这种方式管理消息生命周期。

Kafka 的消息保留主要由 Topic 的数据保留策略决定,例如保留时间、保留容量,也可以根据 Topic 的日志清理策略进行压缩整理。

这意味着:

消息是否还保存在 Kafka 中,通常与某一个 Consumer 有没有读取过它无关。

例如:

text 复制代码
payment-events

Offset 0
Offset 1
Offset 2
Offset 3

订单服务已经处理到 Offset 4。

但通知服务可能才处理到 Offset 2。

Kafka 不需要因为订单服务完成消费就删除前面的消息,否则通知服务就可能无法继续读取。

这也是 Kafka 能支持多个独立 Consumer Group 消费同一个 Topic 的基础。

另外,只要所需数据还处在可读取的保留范围内,消费者就有机会通过调整消费位置重新读取历史消息。

因此,Kafka 的 Offset 不仅用于记录进度,也为失败恢复和历史消息重新处理提供了基础。


五、Consumer Group:多个消费者到底是怎么分工的?

这是 Kafka 最容易混淆、但也最重要的一个概念。

先看一个例子。

支付成功后,我们希望:

text 复制代码
Order Service

更新订单状态。

同时:

text 复制代码
Notification Service

发送通知。

它们都需要消费:

text 复制代码
payment-events

但我们不希望它们互相抢消息。

例如订单服务消费了一条支付成功事件,不能因此让通知服务再也读不到这条消息。

Kafka 通过 Consumer Group 解决这个问题。

1. 不同 Consumer Group:各自独立消费

我们可以创建两个 Consumer Group:

text 复制代码
Consumer Group A:订单处理组

Consumer Group B:通知处理组

它们都订阅 payment-events。

text 复制代码
                  payment-events
                         |
              +----------+----------+
              |                     |
              v                     v
       Consumer Group A      Consumer Group B
         订单处理组              通知处理组
              |                     |
              v                     v
        Order Consumer       Notification Consumer

这两个 Consumer Group 拥有各自的消费进度。

例如:

text 复制代码
Group A:
Partition 0 已提交 Offset 100

Group B:
Partition 0 已提交 Offset 80

这说明订单处理组的消费进度更靠前,而通知处理组还在处理较早的消息。

两个 Consumer Group 互不共享消费进度。

因此,同一条消息可以分别被不同 Consumer Group 消费。

这里需要避免一个常见误解:

Kafka 不一定是把同一条消息物理复制两份,分别发给两个 Consumer Group。

更准确的理解是:

不同 Consumer Group 根据各自的 Offset,从同一份 Topic 分区日志中读取消息。

2. 同一个 Consumer Group:分工消费

现在假设 Notification Service 的消息处理量越来越大。

原来只有一个 Consumer:

text 复制代码
Notification Group
        |
        v
    Consumer A

后来我们部署三个通知服务实例:

text 复制代码
Notification Group
       |
       +-- Consumer A
       |
       +-- Consumer B
       |
       +-- Consumer C

这时候是不是三个 Consumer 都会收到同一条消息,然后发送三次短信?

在正常的分区分配机制下,不会。

因为它们属于同一个 Consumer Group。

Kafka 会在这个 Consumer Group 内分配 Topic 的 Partition。

假设 payment-events 有三个 Partition:

text 复制代码
Notification Group

Partition 0 → Consumer A

Partition 1 → Consumer B

Partition 2 → Consumer C

这样,每个 Consumer 负责处理自己分配到的分区。

从消费任务分工角度看,三个 Consumer 可以并行处理三个 Partition 的消息。

但需要注意,这只是在正常分区分配状态下,同一个 Partition 由组内一个 Consumer 负责。发生故障、消费进度回退等情况时,同一条业务消息仍然可能被重复处理。

所以:

Consumer Group 解决的是消费任务如何分配,而不是自动保证业务操作绝不重复。

3. 为什么三个 Partition 不能让五个 Consumer 同时处理?

假设 Topic 只有:

text 复制代码
Partition 0
Partition 1
Partition 2

但我们在同一个 Consumer Group 中启动了五个 Consumer:

text 复制代码
Consumer A
Consumer B
Consumer C
Consumer D
Consumer E

那么典型情况下:

text 复制代码
Partition 0 → Consumer A

Partition 1 → Consumer B

Partition 2 → Consumer C

Consumer D → 没有分配到分区

Consumer E → 没有分配到分区

后面两个 Consumer 暂时没有分区可消费。

这是因为,在 Kafka 常见的 Consumer Group 分区分配模型中:

一个 Partition 在同一时刻只能分配给组内一个活跃 Consumer。

所以,当 Topic 只有三个 Partition 时,同一个 Consumer Group 即使启动十个 Consumer,也不能让十个 Consumer 同时分别拥有这三个分区的消费任务。

这也是为什么 Kafka 的 Partition 数量会影响消费者组的最大分区级并行度。

4. 一个 Consumer 可以消费多个 Partition 吗?

可以。

假设:

text 复制代码
Topic 有 4 个 Partition

Consumer Group 只有 2 个 Consumer

那么可能分配成:

text 复制代码
Consumer A
    |
    +-- Partition 0
    +-- Partition 1

Consumer B
    |
    +-- Partition 2
    +-- Partition 3

一个 Consumer 完全可以负责多个 Partition。

因此,可以这样记忆:

一个 Partition 在同一组内同一时刻分配给一个 Consumer,但一个 Consumer 可以负责多个 Partition。


六、Rebalance:Consumer 挂了以后,消息由谁处理?

现在我们知道,Kafka 会把分区分配给 Consumer Group 内的消费者。

但这些 Consumer 并不会永远保持不变。

例如通知服务原本运行三个实例:

text 复制代码
Notification Group

Partition 0 → Consumer A

Partition 1 → Consumer B

Partition 2 → Consumer C

假设 Consumer B 突然崩溃。

那么:

text 复制代码
Partition 1

原来由谁负责?

Kafka 需要重新协调消费者组中的分区分配,让仍然存活的 Consumer 接管相应的 Partition。

例如:

text 复制代码
Notification Group

Partition 0 → Consumer A

Partition 1 → Consumer A

Partition 2 → Consumer C

这个过程就涉及 Rebalance(重新平衡或分区再分配)。

哪些情况可能触发 Rebalance?

常见情况包括:

  • Consumer 加入某个 Consumer Group。
  • Consumer 离开消费者组。
  • Consumer 崩溃或不再满足组成员存活条件。
  • 订阅 Topic 的分区数量发生变化。
  • 其他导致分区分配需要重新协调的情况。

例如:

text 复制代码
原来:
2 个 Consumer

新增:
1 个 Consumer

合计:
3 个 Consumer

消费者组就可能重新分配 Partition,让新加入的 Consumer 承担一部分任务。

Rebalance 为什么可能带来问题?

因为消费者原来负责的 Partition 可能被转交给其他 Consumer。

假设:

text 复制代码
Consumer A
读取 Offset 10
      |
      v
执行业务操作
      |
      v
还没有提交消费进度
      |
      v
Consumer A 崩溃

随后 Partition 被重新分配给 Consumer B。

Consumer B 从已提交的消费进度继续读取。

如果 Offset 10 对应的业务已经成功执行,但消费进度没有及时提交,那么这条消息就可能再次处理。

因此,Consumer Group 的故障恢复与 Offset 提交是联系在一起的。

这也解释了一个我们之前多次讨论的问题:

Kafka 能通过消费者组分配机制恢复消费工作,但并不能替业务保证"消息只执行一次"。

如何安全处理重复消费,需要结合业务幂等设计。

这部分会在下一篇详细讨论。


七、Kafka 能保证消息有顺序吗?

很多人在第一次接触 Kafka 时,会听到:

"Kafka 是有序的。"

但这句话必须加上一个前提:

Kafka 主要提供的是 Partition 内的消息顺序,而不是整个 Topic 的全局顺序。

例如一个 Topic 有三个 Partition:

text 复制代码
Partition 0:
A → D → G

Partition 1:
B → E → H

Partition 2:
C → F → I

Partition 0 内部可以按照该分区日志中的位置顺序读取:

text 复制代码
A → D → G

但不能根据三个 Partition 的日志位置,就认为整个 Topic 存在一个统一的:

text 复制代码
A → B → C → D → E → F

的全局消息顺序。

不同 Partition 可以独立写入和消费,消息处理完成的先后顺序还会受到 Consumer 执行时间等因素影响。

为什么我们之前强调 order_id 作为 Key?

假设某笔订单经历:

text 复制代码
OrderCreated
      |
      v
OrderPaid
      |
      v
OrderShipped

如果这三条消息分别进入不同 Partition,那么 Consumer 可能以不同速度处理各个分区。

这样就可能出现某个消费者先处理 OrderPaid,后处理 OrderCreated 的情况。

如果发送消息时统一采用:

text 复制代码
Key = order_id

那么同一个订单的相关消息在分区规则保持稳定的前提下,通常会被路由到同一个 Partition。

例如:

text 复制代码
Partition 1

Offset 100:OrderCreated(10001)

Offset 101:OrderPaid(10001)

Offset 102:OrderShipped(10001)

这为同一订单的按序消费提供了基础。

但需要注意:

同一个 Partition 中按顺序读取消息,不代表应用最终产生的所有业务副作用一定严格按顺序完成。

如果消费者拿到消息后又启动多个异步线程并行处理,或者把失败消息单独放入重试队列,就可能打乱业务处理完成的顺序。

因此,如果业务要求同一个订单的状态严格按顺序推进,就不仅要考虑 Kafka 的分区设计,还需要考虑消费者如何执行和提交业务操作。

Key 应该怎么选?

可以从业务对象的顺序要求出发。

例如:

text 复制代码
订单事件:
Key = order_id

用户行为事件:
Key = user_id

账户变更事件:
Key = account_id

这样相同业务对象的事件更容易被组织在同一分区中。

但也不能完全忽略数据分布问题。

例如某个 Key 的消息量特别大,就可能造成单个 Partition 成为热点。

因此,Kafka 的分区键选择,实际上同时涉及:

消息顺序要求与负载分布之间的取舍。


八、Kafka 和传统任务队列有什么区别?

前面学习后台任务时,我们讲过:

text 复制代码
Producer
    |
    v
Task Queue
    |
    v
Worker

Worker 取出任务,执行完成后确认。

从使用场景上看,Kafka 也能承担异步任务传递的职责。

但它的核心设计与一些传统的"取出即移除"的队列模型不同。

Kafka 更强调持久化的分区日志和消费位置。

例如:

text 复制代码
Topic: payment-events

Offset 0
Offset 1
Offset 2
Offset 3

Order Consumer 可以处理到 Offset 4。

Notification Consumer 可以处理到 Offset 2。

Statistics Consumer 可以处理到 Offset 3。

这些进度互不影响。

Kafka 不需要在某个 Consumer 读取后立即删除消息。

因此,它比较适合:

  • 多个系统独立消费同一类业务事件。
  • 高吞吐量的异步消息处理。
  • 根据保留策略保存一段时间的事件。
  • 在保留范围内重新读取历史事件。
  • 按 Partition 扩展消费并行度。

但这并不意味着所有异步任务都应该使用 Kafka。

如果只是一个简单的后台任务队列,任务数量不大,也不需要多个独立消费者组或历史消息重放,那么更简单的任务队列实现可能更合适。

此外,Kafka 消息并不天然具备完整的业务任务状态。

例如:

text 复制代码
WAITING
RUNNING
SUCCESS
FAILED

如果需要追踪一个长任务的完整生命周期,通常还需要业务数据库或专门的任务状态管理机制。

所以 Kafka 的价值不是取代所有任务队列,而是在需要持久化事件流、消费并行和独立消费进度时提供相应能力。


九、用一个完整例子串起来

现在回到开头的支付成功事件。

假设有:

text 复制代码
Payment Service

Order Service

Notification Service

Payment Service 完成支付确认后,准备发布:

text 复制代码
PaymentSucceeded

消息结构:

json 复制代码
{
  "event_id": "E10001",
  "order_id": 10001,
  "status": "PAID"
}

我们把它发送到:

text 复制代码
Topic = payment-events
Key = 10001

Topic 有三个 Partition。

根据 Key 分区规则,消息进入:

text 复制代码
Partition 1

并被追加到某个 Offset。

text 复制代码
payment-events

Partition 0:
...

Partition 1:
Offset 50 → PaymentSucceeded(10001)

Partition 2:
...

现在有两个消费者组。

text 复制代码
Group A:order-group

Group B:notification-group

它们都订阅 payment-events。

Order Group

订单处理组读取这条消息:

text 复制代码
PaymentSucceeded
       |
       v
Order Consumer
       |
       v
MySQL 更新订单状态

例如将订单从:

text 复制代码
UNPAID

更新为:

text 复制代码
PAID

然后根据消费处理策略提交 Offset。

Notification Group

通知组也能独立读取同一条消息:

text 复制代码
PaymentSucceeded
       |
       v
Notification Consumer
       |
       v
发送支付成功通知

即使订单组已经完成消费,通知组也不受影响。

因为它们维护各自的消费进度。

Notification Service 扩容

假设通知处理越来越慢。

原来只有一个 Consumer:

text 复制代码
notification-group

Consumer A

现在扩容到三个:

text 复制代码
notification-group

Consumer A
Consumer B
Consumer C

Kafka 可以把三个 Partition 分配给三个 Consumer,实现分区级并行处理。

如果再增加两个 Consumer,而 Topic 仍然只有三个 Partition,那么通常不会进一步增加该组的分区消费并行度。

Consumer 突然崩溃

假设 Consumer B 正在处理消息时崩溃。

消费者组会重新协调分区分配。

其他 Consumer 接管相应分区,并根据已提交的 Offset 继续读取。

如果之前的业务处理已完成,但 Offset 没提交,这条消息就可能再次被处理。

因此,通知服务还需要根据 event_id 等稳定业务标识设计幂等逻辑,避免重复消费造成重复通知等副作用。

到这里,Producer、Broker、Topic、Partition、Offset 和 Consumer Group 就串成了一条完整的消息链路。


十、总结:Kafka 最重要的不是"发消息",而是消息怎么存、怎么分配、怎么继续消费

回顾这一篇,Kafka 的核心概念其实可以分成三个层次。

第一层:消息如何进入 Kafka。

Producer 将消息发送到 Topic,Kafka 根据分区规则将消息写入对应的 Partition。

Broker 负责提供消息存储与访问服务。

第二层:消息如何组织。

Topic 是逻辑主题。

Partition 是实际的分区日志。

Offset 标识一条消息在某个 Partition 中的位置。

同一个 Partition 中的消息可以按其日志位置读取,但不同 Partition 之间没有天然的全局顺序。

第三层:消息如何消费。

Consumer Group 负责组织消费者协作。

不同 Consumer Group 可以独立读取同一份 Topic 数据,并拥有各自的消费进度。

同一个 Consumer Group 内,Partition 会分配给组内 Consumer,多个 Consumer 可以分担不同 Partition 的消费工作。

把这些关系总结到一起:

概念 核心作用
Producer 生产并发布消息
Broker Kafka 集群中的服务器节点
Topic 消息的逻辑分类
Partition 分区存储消息,提供分区级顺序和并行度
Offset 标识分区中的消息位置
Consumer 读取并处理消息
Consumer Group 组织消费者分工,维护独立消费进度
Rebalance 组成员或分区分配变化时重新协调消费任务

其中最值得记住的是下面三个结论。

第一,Kafka 的消息通常不会因为被某个 Consumer 读取就立即删除。

消息的保留主要由 Topic 的数据保留和清理策略决定。

第二,同一个 Topic 可以被多个 Consumer Group 独立消费。

每个消费者组都有自己的消费位置,因此订单、通知、统计等服务可以根据各自需要处理同一类事件。

第三,一个 Partition 在同一 Consumer Group 内通常同时只分配给一个 Consumer。

因此,Partition 数量会影响消费者组的最大分区级并行度,但增加 Consumer 数量并不意味着吞吐量一定能够继续提升。

理解了这些知识之后,再学习 Kafka 的 ACK、副本复制、Offset 提交和重复消费,就不会觉得它们是互不相关的概念。

下一篇我们会进一步讨论:Kafka 为什么可能重复消费?Producer ACK 到底确认了什么?为什么 MySQL 已经更新成功,Consumer 崩溃后仍然可能再次处理同一条消息?

这些问题才是 Kafka 真正用于业务系统时,需要重点考虑的可靠性问题。

相关推荐
用户1494484813202 小时前
为什么大厂很少直接用 2PC/TCC?聊聊分布式事务在真实业务里的落地套路
分布式·后端
code_slave(码畜)4 小时前
微服务架构落地:分库分表实战(下) —— 分布式主键、跨分片查询与分页聚合防坑
数据库·spring boot·分布式·spring cloud·微服务·架构·分库分表
德迅云安全-上官5 小时前
新一代智能分布式云接入:应用加速守护 TCP 业务安全与体验
分布式·tcp/ip·安全
凯凯姜5 小时前
分布式定时任务重复执行与故障恢复
分布式
ClouGence6 小时前
还在用命令行管理 Kafka?开源 CloudDM 让操作更简单
数据库·kafka·开源
Sayai6 小时前
Kafka 磁盘空间治理实战:retention 保留策略、log.dirs 在线扩容、分区跨盘迁移与不停机整体换盘
大数据·运维·分布式·kafka·消息队列
爱吃牛肉的大老虎7 小时前
Redis之分布式 Session问题
数据库·redis·分布式
阿标在干嘛7 小时前
分布式锁的“场景适配“:我们为什么在部分链路中选择了数据库乐观锁?
数据库·分布式
孤狼GPT8 小时前
ChatGPT、Codex排查实录:Kafka消费者日志明明显示处理成功,为什么同一条消息还是被重复消费?
kafka·springboot·codex·chatgptplus·chatgptpro