3、Kafka核心概念:Topic、Partition、Offset、Producer、Consumer、Broker

好,咱们直接进入正题。Kafka 这玩意儿,说复杂也复杂,说简单也简单。核心就这几个概念:Topic、Partition、Offset、Producer、Consumer、Broker。你把这几个东西搞明白了,Kafka 就算入门了。

我刚开始接触 Kafka 的时候,也被这些名词绕得头晕。后来我总结了一个方法——把 Kafka 想象成一个快递系统,一下子就通了。咱们今天就按这个思路来拆解。

3.1 Broker:快递中转站

Broker 就是 Kafka 集群中的一台服务器。说白了,它就是存数据、转发数据的节点。

一个 Kafka 集群由多个 Broker 组成。每个 Broker 都有一个唯一的 ID,通常是数字。我习惯从 0 开始编号,这样好记。

关键点:Broker 本身不存业务逻辑,它只负责两件事——存消息转发消息

我在项目中遇到过一个问题:刚开始只部署了 3 个 Broker,觉得够用了。结果业务量一上来,其中一个 Broker 直接被打满。嗯,这里要注意——Broker 的数量不是拍脑袋定的,得根据你的吞吐量来算

Broker 角色 职责
Leader 负责读写请求,一个 Partition 只有一个 Leader
Follower 负责同步 Leader 数据,Leader 挂了就顶上

3.2 Topic:快递分类标签

Topic 是消息的逻辑分类。你想想看,快递到了中转站,怎么区分哪些是生鲜、哪些是日用品?靠标签。Topic 就是那个标签。

每个 Topic 都有一个名字,比如 order_eventsuser_logs。生产者往 Topic 里发消息,消费者从 Topic 里读消息。它们只认 Topic 名字,不关心别的。

我的建议:Topic 命名一定要规范。我见过有人用 test1test2 这种名字,三个月后自己都看不懂了。推荐格式:{业务域}_{事件类型},比如 order_created

3.3 Partition:快递分拣通道

一个 Topic 可以分成多个 Partition。Partition 是 Kafka 实现高吞吐的关键。

为什么要有 Partition?为了并行。一个 Topic 如果只有一个 Partition,那所有消息都得排队处理,速度上不去。分成多个 Partition,就可以同时处理多条消息。

每个 Partition 是一个有序的、不可变的日志文件。消息追加到 Partition 尾部,不能修改。这设计很巧妙——写操作只有追加,没有随机写,所以快

避坑指南:我曾经犯过一个错误——把 Partition 数量设得特别大,比如 100 个。结果呢?Broker 上文件句柄数暴增,性能反而下降了。Partition 数量不是越多越好,一般建议 Broker 数量 × 2 到 3 倍

3.4 Offset:快递单号

Offset 是消息在 Partition 中的唯一编号。它从 0 开始,每条消息一个,递增且不重复。

消费者读取消息时,就是通过 Offset 来定位的。比如消费者说:「我从 Offset 100 开始读」,Broker 就知道该从哪条消息开始发。

这里有个重要的点:Offset 只在 Partition 内唯一。不同 Partition 的 Offset 是独立的。所以你不能说「全局 Offset 是多少」,只能说「Partition 0 的 Offset 是 100」。

核心理解:Offset 让消费者可以「断点续传」。消费者挂了重启,只要记住上次消费的 Offset,就能从断点继续,不会丢消息,也不会重复消费。

3.5 Producer:发件人

Producer 是消息的生产者。它负责把消息发到指定的 Topic。

Producer 有几个关键参数,我挑重要的说:

  • acks:确认机制。0 表示不等待确认,1 表示 Leader 确认,all 表示所有副本确认。我一般用 acks=all,虽然慢一点,但保证不丢数据。
  • retries:重试次数。网络抖动是常态,我习惯设成 3 次。
  • batch.size:批量发送大小。攒够一批再发,吞吐量更高。
// 一个简单的 Producer 示例
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("acks", "all");
props.put("retries", 3);
props.put("batch.size", 16384);

Producer<String, String> producer = new KafkaProducer<>(props);
producer.send(new ProducerRecord<>("order_events", "order_id_123", "{\"status\":\"created\"}"));
producer.close();

我个人习惯把 Producer 做成单例,避免频繁创建销毁连接。你想想看,每次发消息都 new 一个 Producer,那性能得多差。

3.6 Consumer:收件人

Consumer 是消息的消费者。它从 Topic 的 Partition 里拉取消息。

Consumer 有个重要的概念——Consumer Group。同一个 Group 里的 Consumer,共同消费一个 Topic 的消息。每条消息只会被 Group 内的一个 Consumer 消费。

举个例子:假设 Topic 有 4 个 Partition,Group 里有 2 个 Consumer。那每个 Consumer 会负责 2 个 Partition。如果 Group 里有 4 个 Consumer,那就一人一个 Partition。

我的经验:Consumer 的数量最好和 Partition 数量一致。多了浪费,少了会有 Consumer 空闲。我曾经踩过坑——Group 里 10 个 Consumer,但 Topic 只有 3 个 Partition,结果 7 个 Consumer 闲着没事干。

// 一个简单的 Consumer 示例
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("group.id", "order_consumer_group");
props.put("enable.auto.commit", "true");
props.put("auto.commit.interval.ms", "1000");

Consumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Arrays.asList("order_events"));

while (true) {
    ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
    for (ConsumerRecord<String, String> record : records) {
        System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());
    }
}

3.7 它们之间的关系

说了这么多,咱们捋一捋这几个概念的关系:

  • Broker 是服务器,负责存数据
  • Topic 是分类标签,一个 Topic 可以跨多个 Broker
  • Partition 是 Topic 的分片,每个 Partition 存在一个 Broker 上
  • Offset 是 Partition 内消息的编号
  • Producer 往 Topic 的 Partition 里写消息
  • Consumer 从 Topic 的 Partition 里读消息

说白了,整个流程就是:Producer 把消息写到 Broker 上的某个 Partition,Consumer 从那个 Partition 里按 Offset 顺序读取

一句话总结:Topic 是逻辑容器,Partition 是物理存储单元,Offset 是消息的身份证,Broker 是干活的人,Producer 和 Consumer 是上下游。

嗯,这些概念你理解了,Kafka 的骨架就有了。下一章咱们聊聊消息的存储机制,看看 Kafka 到底是怎么把数据写到磁盘上的。