Kafka 是由 Apache 软件基金会开源的一个分布式流处理平台,本质上是一套高吞吐、可持久化的分布式消息队列系统。它处理上下游数据的时间差问题,解决上下游消息的存储与投递。本文按「概念 → 架构 → 存储 → 可靠性 → 消费」的顺序,把 Kafka 的核心原理串成一条完整链路。
一、消息系统的两种模式
理解 Kafka 之前,先理解消息系统的两种基础通信模式:
- 点对点传递(Queue):消息被持久化到一个队列中,一条消息只被消费一次即从队列删除(消费后被删除)。多个消费者不会重复消费同一条消息。
- 发布订阅传递(Pub/Sub):消息被持久化到一个 Topic 中,消费者可以订阅一个或多个 Topic,消费该 Topic 里的所有消息。同一条消息可被多个消费者消费,消息不会因被消费而立刻删除。Kafka 采用拉取(Poll)模式,由消费者自己控制消费速度。
Kafka 的设计融合了两者:通过消费者组实现「组内点对点、组间发布订阅」。
二、生产者-消费者模式与异步通信
Kafka 建立在异步通信原理之上:
- 观察者模式:每一个对象的改变(消息产生),所有依赖于它的对象都会得到通知并自动更新。
- 生产者消费者模式:一对多依赖,通过一个线程队列进行消费,两种角色通过内存缓冲区队列进行通信。生产者只负责把消息传给缓冲队列,消费者从队列取,二者解耦。
这套模式带来三个特性:并发性好、耦合度低、消息传输速率不匹配时可缓冲削峰。
三、Kafka 整体架构
Kafka 的架构由以下核心组件构成:
- Producer(生产者):消息的产生者,向 Kafka 的 Topic 发消息。发送到 Kafka 的消息最终追加到 segment 文件中,每个 partition 中的消息都有序,存储着许多 partition,每条消息都会有自己的偏移量(offset)。
- Broker:Kafka 集群包含一个或多个服务器,每个服务节点称为一个 Broker。
- Topic(主题):逻辑概念,一类消息的集合。物理上一个 Topic 拆成多个 Partition(分区),每个 partition 对应一个 segment 文件存储,分布在不同 Broker 上。
- Partition 副本(Replica):每个 partition 有一个 Leader 和若干 Follower。生产者写入和消费者读取都只与 Leader 交互,Follower 只负责从 Leader 同步数据做备份。
- Consumer / Consumer Group(消费者组):多个消费者组成一个组一起处理某个 Topic 的数据。一个 partition 只会被同一个消费者组里的一个消费者消费(一组内消费者不会消费到同一条数据)。
- Zookeeper:存储 Kafka 的元数据信息,帮助选举 Leader、管理集群。
Kafka 提供两套 Consumer API:① High-level Consumer API,消费者组的抽象对象;② SimpleConsumer API,直接精细化控制。
四、Segment 与底层数据存储
Kafka 的高性能很大程度来自它的存储设计。一个 Partition 在物理上被切分成多个 Segment,每个 segment 由一对文件组成:
.log文件:存储真实数据(消息体)。.index文件:稀疏索引,快速定位 log 文件中消息的位置。
关键参数与机制:
log.segment.bytes:单个 segment 文件的最大字节数,默认 1GB。log.segment.ms:Kafka 到了这个时间(默认 7 天)即使 segment 没满也会切分新文件。- 查找机制:先用二分查找定位到 segment,再用稀疏 index 折半查找定位到具体消息,
log 文件 + offset精确定位到一条数据。这种「稀疏索引 + 二分查找」让 Kafka 在海量数据下依然能快速检索。
Segment 命名以该分区第一条消息的 offset 命名。稀疏索引虽然节省空间,但保存了每条消息的相对位移,维护成本低。
五、生产者可靠性:ACK 机制与副本同步
生产者把消息发给 Leader 后,怎么确认「消息真的没丢」?这靠 ACK 应答机制:
| acks 取值 | 含义 | 可靠性 vs 性能 |
|---|---|---|
0 |
不需要 Leader 返回 ACK,发完不管 | 最快、最不安全(可能丢) |
1 |
Leader 写入本地即返回 ACK | 一般,Leader 挂了可能丢 |
-1(all) |
Leader + 所有同步 Follower 都确认 | 最慢、最安全 |
为了实现 acks=-1,Kafka 引入 ISR(In-Sync Replicas,同步副本集合):
- AR = ISR + OSR:AR 是所有副本,ISR 是与 Leader 保持同步的副本,OSR 是同步滞后被踢出的副本。
- Follower 落后超过阈值(
replica.lag.time.max.ms,默认 10s)就被移出 ISR 进入 OSR;追上了再回到 ISR。 - 只有 ISR 里的副本才有资格参与 Leader 选举。
六、副本选举:HW 与 LEO
当 Leader 挂掉需要重新选举时,涉及两个关键水位线:
- LEO(Log End Offset):每个副本最后一条消息的下一个位置(日志末端偏移)。
- HW(High Watermark,高水位线):所有 ISR 副本中最小的 LEO。消费者只能看到 HW 之前的数据——保证了「已提交」的数据在任何副本上都存在。
选举原则:从 ISR 里挑选新 Leader。unclean.leader.election.enable=false 表示不允许从 OSR(落后副本)里选 Leader,防止数据丢失。
七、生产者幂等性与事务
幂等性:指多次调用产生的结果和调用一次一致(避免重复写入)。作用是保证单个分区不会重复写入数据。实现原理:
- 每个生产者被分配一个 PID,消息按
<PID, Partition, SeqNumber>去重。 - Broker 端会缓存每个 PID 在每个分区上的最新 SeqNumber。
- 若
SN_new = SN_old + 1,Broker 正常接收;若SN_new ≤ SN_old,说明重复,丢弃;若SN_new > SN_old + 1,说明中间有丢失,拒绝并报错。
事务:解决跨 Partition 写操作的原子性——写入多个 Partition 要么全部成功、要么全部失败,不会出现部分成功部分失败。幂等性只保证单分区不重复,事务才能保证跨分区的一致性。
八、消费者:消费方式与分区分配策略
消费方式:消费者采用拉取(Pull)而非推送。推送模式下数据速率由 Broker 决定,消费者可能扛不住;拉取模式由消费者根据自己的消费能力决定拉取速度,更灵活。拉取的缺点是若没有数据消费者可能空轮询,Kafka 用 timeout 长轮询解决——没有数据时等待一段时间再返回。
分区分配策略(决定 partition 与 consumer 的对应关系,消费者数量变化时会触发重新分配 / Rebalance):
- RoundRobin(轮询):把所有 partition 和 consumer 列出来,轮流分配,相对均衡。
- Range(范围):按单个 Topic 划分,用 partition 数除以 consumer 数,除不尽时前面的消费者多分。容易造成数据倾斜。
- Sticky(粘性,0.11 后新增):分配尽量均衡,且重分配时尽量保留原有分配,减少变动开销。
九、Offset 管理
Offset 表示消费者在分区中消费到的位置:
- 生产者 offset:消息写入时的位置,追加到 log 文件末尾。
- 消费者 offset:
ConsumerA消费到的位置,下次接着这里消费。消费者 offset 保存在一个特殊 Topic__consumer_offsets里(默认 50 个分区,通过hashCode(groupid) % 50决定存到哪个分区)。
提交方式:
- 同步提交(commitSync):会阻塞直到提交成功,可靠但慢。
- 异步提交(commitAsync):不阻塞、吞吐高,但提交失败不会自动重试,需处理提交乱序问题。
十、Kafka 与 Flume 整合
Flume 采集数据(多来源、多通道),Flume 把数据写给 Kafka,Kafka 进行消峰、缓存、保证数据的安全等特性。这是大数据采集链路里典型的组合:Flume 负责「采」,Kafka 负责「缓冲和分发」。
常用操作命令速查
# 启动 Kafka
kafka-server-start.sh /opt/jjx/kafka_2.12-0.11.0.3/config/server.properties
# 创建主题
kafka-topics.sh --zookeeper node01:2181 --create --replication-factor 2 --partitions 3 --topic userlog
# 查看主题列表
kafka-topics.sh --zookeeper node01:2181 --list
# 查看某个主题详情
kafka-topics.sh --zookeeper node01:2181 --describe --topic userlog
# 启动生产者(控制台)
kafka-console-producer.sh --broker-list node01:9092,node02:9092,node03:9092 --topic userlog
# 启动消费者(从头消费)
kafka-console-consumer.sh --zookeeper node01:2181,node02:2181,node03:2181 --from-beginning --topic userlog
本文整理自个人大数据学习知识导图,完整思维导图见下方原图。

"""
评论