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_events、user_logs。生产者往 Topic 里发消息,消费者从 Topic 里读消息。它们只认 Topic 名字,不关心别的。
我的建议:Topic 命名一定要规范。我见过有人用 test1、test2 这种名字,三个月后自己都看不懂了。推荐格式:{业务域}_{事件类型},比如 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 到底是怎么把数据写到磁盘上的。