3. Kafka 核心概念:Topic、Partition、Producer、Consumer、Broker、Offset、Consumer Group
好,咱们今天来啃 Kafka 最核心的七个概念。说实话,我刚接触 Kafka 时,被这些名词绕得晕头转向。什么 Topic、Partition、Offset……感觉像在学一门新语言。但后来我发现,只要搞懂这七个东西,Kafka 的架构你就拿下了大半。
我习惯把这七个概念分成三组来理解:存储层(Topic、Partition、Broker)、客户端层(Producer、Consumer)、协调层(Offset、Consumer Group)。咱们一个一个来。
一句话总结:Kafka 本质上就是一个「分布式消息日志系统」。Producer 往日志里写,Consumer 从日志里读,Broker 负责存日志,Offset 是日志的行号,Consumer Group 决定谁读哪部分日志。
3.1 Topic —— 消息的逻辑分类
Topic 是 Kafka 里最上层的概念。说白了,它就是消息的「分类标签」。你想想看,一个系统里可能有用户点击事件、订单事件、支付事件……如果全混在一起,谁分得清?Topic 就是用来做这件事的。
我习惯把 Topic 类比成数据库里的「表」。Producer 往某个 Topic 写数据,Consumer 从某个 Topic 读数据。比如咱们这个课程的项目,就可以建一个 user_events 的 Topic,专门存放用户行为数据。
命名建议:我个人习惯用「项目名_模块名_事件类型」的格式,比如 shop_order_created、analytics_page_view。这样一看就知道是哪个项目、哪个模块、什么事件。
3.2 Partition —— 并行与扩展的基石
Topic 只是一个逻辑概念。真正存数据的地方,是 Partition(分区)。
一个 Topic 可以分成多个 Partition。每个 Partition 是一个有序的、不可变的日志文件。新消息永远追加到 Partition 末尾。
为什么要分区?两个原因:并行和扩展。
- 并行:多个 Consumer 可以同时读不同 Partition,互不干扰。
- 扩展:Partition 可以分布在不同的 Broker 上,一台机器不够?加机器,把 Partition 挪过去就行。
我记得有一次线上 Kafka 集群扛不住流量,一查发现 Topic 只配了 3 个 Partition。后来扩到 12 个,吞吐量直接翻了 3 倍。嗯,分区数不是越多越好,但太少肯定不行。
注意:Partition 内的消息是有序的,但跨 Partition 的消息不保证全局有序。如果你需要严格的消息顺序,要么只用 1 个 Partition,要么在业务层自己处理排序逻辑。
3.3 Broker —— Kafka 服务器节点
Broker 就是运行 Kafka 服务的机器。一个 Kafka 集群由多个 Broker 组成。
每个 Broker 会存储一部分 Partition 的数据。比如一个 Topic 有 6 个 Partition,3 台 Broker,那每台 Broker 大概存 2 个 Partition。
Broker 之间会互相通信,负责 Partition 的 Leader 选举、数据复制这些脏活累活。你不需要手动干预,Kafka 自己会搞定。
| 概念 | 类比 | 说明 |
|---|---|---|
| Topic | 数据库的表 | 消息的逻辑分类 |
| Partition | 表的分区文件 | 实际存储消息的地方 |
| Broker | 数据库服务器 | 运行 Kafka 进程的节点 |
3.4 Producer —— 消息的生产者
Producer 负责把消息发到 Kafka。它需要决定:发到哪个 Topic?发到哪个 Partition?
Producer 可以指定 Partition,也可以让 Kafka 自己算。默认策略是「轮询」或者根据消息的 Key 做哈希。
// 一个简单的 Producer 示例
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
Producer<String, String> producer = new KafkaProducer<>(props);
producer.send(new ProducerRecord<>("user_events", "click", "{\"user_id\": 123, \"page\": \"/home\"}"));
producer.close();
我曾经踩过一个坑:Producer 默认是异步发送的,如果不调用 flush() 或 close(),消息可能根本没发出去就退出了。调试时死活找不到原因,后来才发现是忘了关 Producer。
3.5 Consumer 与 Consumer Group —— 消息的消费者
Consumer 从 Kafka 拉取消息。但 Consumer 不是单打独斗的,它属于一个 Consumer Group(消费组)。
同一个 Group 内的 Consumer,共同消费一个 Topic 的消息。每条消息只会被 Group 内的一个 Consumer 处理。这就是「点对点」模式。
不同 Group 之间是独立的。两个 Group 可以同时消费同一个 Topic,互不影响。这就是「发布-订阅」模式。
关键规则:一个 Partition 只能被同一个 Group 内的一个 Consumer 消费。如果 Consumer 数量多于 Partition 数量,多出来的 Consumer 会闲着没事干。
我见过一个新手踩的坑:开了 10 个 Consumer,但 Topic 只有 3 个 Partition,结果 7 个 Consumer 一直在空转。浪费资源不说,还以为是 Kafka 性能不行。
3.6 Offset —— 消费位置的「书签」
Offset 是 Kafka 里最巧妙的设计之一。每条消息在 Partition 内都有一个唯一的 Offset,从 0 开始递增。
Consumer 消费完一条消息后,会提交当前 Offset。下次重启时,从上次提交的 Offset 继续消费。这样就不会重复消费,也不会漏掉消息。
Offset 的提交方式有两种:
- 自动提交:默认每 5 秒提交一次。简单,但可能丢消息或重复消费。
- 手动提交:业务代码里显式调用
commitSync()或commitAsync()。更可控,但代码复杂一点。
我的建议:生产环境一定要用手动提交。自动提交在 Consumer 崩溃时,可能会丢失最近 5 秒内的消息。我曾经因为这个丢过一批用户行为数据,排查了一整天才找到原因。
3.7 七个概念的关系总结
咱们用一张表把这七个概念串起来:
| 概念 | 一句话 | 我踩过的坑 |
|---|---|---|
| Topic | 消息的分类标签 | 命名不规范,后来维护成本高 |
| Partition | 消息的实际存储单元 | 分区数太少,吞吐上不去 |
| Broker | Kafka 服务器节点 | Broker 宕机时没配副本,数据丢了 |
| Producer | 消息的生产者 | 忘了 close(),消息没发出去 |
| Consumer | 消息的消费者 | Consumer 比 Partition 多,空转浪费 |
| Consumer Group | 消费者的逻辑分组 | Group ID 配错,消费重复了 |
| Offset | 消息在 Partition 内的位置 | 自动提交丢消息 |
这七个概念,是 Kafka 的骨架。后面的章节,咱们会在这个骨架上不断加肉。比如怎么配副本保证高可用、怎么调参数提升吞吐量、怎么用 Kafka Streams 做实时计算……但不管多复杂,底层都是这七个东西在转。
嗯,今天就到这儿。你把这七个概念理清楚,Kafka 的大门就算正式推开了。