3. Kafka 核心概念与架构
Kafka 是什么?说白了,它就是一套「削峰填谷」的消息中间件。我最早接触 Kafka 是在 2016 年,那时候公司要接实时用户行为数据,日均几十亿条。选型时对比了 RabbitMQ 和 RocketMQ,最后还是上了 Kafka。为什么?因为它天生为高吞吐、持久化、可回溯设计。这一章,我把 Kafka 最核心的几个概念掰开揉碎讲清楚。
3.1 消息模型:从「队列」到「发布-订阅」
Kafka 的消息模型,本质上是「发布-订阅」模式。但它跟传统 JMS 队列有个关键区别——消费者拉取,而不是 Broker 推送。
核心概念速览
- Producer:消息生产者,负责把数据发到 Topic
- Consumer:消息消费者,从 Topic 拉取数据
- Topic:消息的逻辑分类,类似数据库里的表
- Partition:Topic 的物理分片,是并行度的基础
- Broker:Kafka 服务器节点,集群中的一台机器
我习惯把 Topic 比作一个「大水池」。Producer 往里面倒水,Consumer 从里面接水。但水池不能无限大,所以 Kafka 把水池切成了多个小水槽——这就是 Partition。
个人经验:我遇到过很多新手把 Topic 和 Partition 混为一谈。记住一句话:Topic 是逻辑概念,Partition 是物理存储。一个 Topic 可以有 N 个 Partition,每个 Partition 就是一个有序的、不可变的日志文件。
3.2 分区与副本机制
分区是 Kafka 并行度的基石。你想想看,如果只有一个分区,那所有消费者都得排队读,吞吐量上不去。分区多了,消费者可以并行消费。
但分区多了也有问题——机器挂了怎么办?数据不就丢了吗?所以 Kafka 引入了副本机制。
3.2.1 副本的角色
| 角色 | 说明 | 关键行为 |
|---|---|---|
| Leader | 每个分区的「主副本」 | 负责所有读写请求 |
| Follower | Leader 的「从副本」 | 只从 Leader 同步数据,不对外服务 |
| ISR | In-Sync Replicas | 与 Leader 保持同步的副本集合 |
这里有个关键点:只有 Leader 副本对外提供服务。Follower 只负责同步,不参与读写。为什么这样设计?我刚开始也觉得浪费,后来才明白——这样能保证数据一致性。如果 Follower 也能读,那读到旧数据的概率就大了。
避坑指南:我曾经在生产环境遇到过一个问题——某个分区的 ISR 列表只剩 Leader 自己了。查了半天,发现是 Follower 副本所在机器磁盘 IO 打满,导致同步延迟超过 replica.lag.time.max.ms(默认 30 秒),被踢出了 ISR。所以,监控 ISR 数量是 Kafka 运维的基本功。
3.2.2 副本的分布策略
Kafka 的副本分布有个原则:尽量分散到不同 Broker 上。比如你有 3 台 Broker,副本因子是 3,那每个分区的 3 个副本会各放一台机器。这样任何一台机器挂了,数据都不丢。
我建议你在部署时,副本因子至少设为 2。如果条件允许,设为 3 更稳妥。但注意,副本越多,写入延迟会稍微增加,因为 Leader 要等所有 ISR 副本确认后才返回。
3.3 生产者与消费者原理
3.3.1 生产者:如何把数据发出去?
Producer 的工作流程,我总结为三步:
- 确定分区:根据 key 的哈希值,或者轮询策略,决定数据发到哪个 Partition
- 批量发送:攒一批数据再发,减少网络开销
- 等待确认:根据
acks参数,决定等不等 Broker 的确认
这里有个参数 acks,很多人搞不清楚。我直接给你结论:
| acks 值 | 含义 | 适用场景 |
|---|---|---|
| 0 | 发出去就不管了 | 丢几条数据无所谓,比如日志 |
| 1 | Leader 写入成功就算成功 | 大部分业务场景,性能与可靠性的平衡点 |
| all | 所有 ISR 副本都写入成功才算成功 | 金融、交易等强一致性场景 |
我的习惯:线上环境我一般用 acks=1。除非是跟钱相关的数据,才用 acks=all。但要注意,acks=all 配合 min.insync.replicas 使用才有意义。如果 min.insync.replicas=1,那 acks=all 其实跟 acks=1 没区别。
3.3.2 消费者:如何保证不丢数据?
Consumer 的核心机制是消费者组。同一个组内的消费者,共同消费一个 Topic 的分区。Kafka 保证:一个分区只能被同一个组内的一个消费者消费。
为什么会这样?你想想看,如果两个消费者同时消费同一个分区,那消息的 offset 就乱套了。谁提交 offset?谁负责重试?所以 Kafka 强制了「分区独占」原则。
消费者有个关键动作——提交 offset。offset 是消费者在分区中的「书签」,记录了消费到哪里了。
我曾经踩过的坑:有一次线上消费延迟飙升,查了半天发现是消费者提交 offset 的方式有问题。代码里用的是「自动提交」,每 5 秒提交一次。结果业务处理耗时超过 5 秒,offset 先提交了,但业务还没处理完。这时候消费者挂了,重启后从新 offset 开始消费,中间那批数据就丢了。解决方案很简单:改成手动提交,等业务处理完再提交 offset。
3.4 集群部署:从单机到集群
单机 Kafka 只能用来测试。生产环境至少 3 台 Broker 起步。我一般推荐 3 台起步,5 台标准,7 台以上算大规模。
3.4.1 部署架构图
下面这张图展示了 Kafka 集群的核心组件与数据流。我习惯用这种图来跟团队对齐架构认知。
3.4.2 部署关键参数
我整理了一份部署时的核心参数清单,这些是我在多个项目中沉淀下来的:
| 参数 | 推荐值 | 说明 |
|---|---|---|
num.partitions |
3 ~ 6 | Topic 默认分区数,根据吞吐量预估 |
default.replication.factor |
2 或 3 | 自动创建 Topic 时的默认副本数 |
log.retention.hours |
72 | 数据保留 3 天,按需调整 |
log.segment.bytes |
1GB | 单个日志段文件大小 |
min.insync.replicas |
2 | 配合 acks=all 使用,保证至少 2 个副本同步 |
unclean.leader.election.enable |
false | 禁止非 ISR 副本成为 Leader,防止数据丢失 |
个人建议:unclean.leader.election.enable 这个参数,我建议永远设为 false。虽然设为 true 能在极端情况下快速恢复服务,但代价是可能丢数据。我宁愿等 ISR 恢复,也不愿意丢数据。你想想看,运维的底线是什么?是数据不丢。
3.4.3 部署步骤简述
部署 Kafka 集群,我一般按这个顺序来:
- 部署 ZooKeeper 集群(3 或 5 台),Kafka 依赖 ZK 做元数据管理和 Leader 选举
- 配置 Kafka Broker,修改
server.properties中的broker.id、log.dirs、zookeeper.connect - 启动 Broker,逐个启动,观察日志是否有报错
- 创建 Topic,用
kafka-topics.sh创建业务 Topic,指定分区数和副本数 - 验证集群,用
kafka-producer-perf-test.sh和kafka-consumer-perf-test.sh做压测
避坑指南:我曾经在部署时犯过一个低级错误——broker.id 重复了。两台 Broker 配了相同的 ID,结果集群一直报错,Leader 选举失败。排查了半天才发现。所以,部署前一定要检查 broker.id 是否唯一。另外,log.dirs 建议用多块磁盘,分散 IO 压力。
3.5 本章小结
Kafka 的核心概念,说白了就这几样:Topic、Partition、副本、Producer、Consumer。理解它们之间的关系,是运维 Kafka 的基础。
我个人的经验是:先搞懂数据流,再搞懂配置。数据怎么进来的?存在哪里?怎么被消费的?把这三个问题想清楚,Kafka 的架构就通了。