一、分片
首先,kafka就是消息队列的一种,将消息分为多个不同的topic,每一个topic中,又细分为不同的patition(注:实际存储的时候,kafka是以partition为单位存储的,topic可以理解为redis中的key,起到分类作用,例如:a:0,表示a主题下的第一个partition),partition存放在broker中,同一主题下的不同的partition可以存放在同一broker中,也可以存在于不同的broker中(但是同一partition的不同副本需要存放在不同的broker中,后面会提到)。
分片规则
- 数据指定了partition,则存入对应的partition
- 如果没有指定partition而是指定了key,则按照key的hash值存入
- 如果没有指定partition和key,则按照顺序存入partition中,先0,然后1,然后2。。。。
读写规则
在集群模式下,一个Partition只对应一个Broker,一个Broker可以存放多个Partition。kafka是先随机挑选一个broker放置分区0,然后再按顺序放置其他分区。
二、broker
集群的访问
客户端维护各个Broker的映射表,此外,和redis类似,broker之间都是相互知道信息的,访问一台Broker的信息就可以得到所有broker的信息列表。
三、生产者
3.1 客户端发送消息到kafka的流程
- 将发送的消息封装成kafka消息包
- 将消息包序列化,在网络中传输
- 传输到kafka之后选择partition
3.1.1 消息的结构

- Key:根据Key的Hash对Partition数目取模来决定是哪个Partition,也就是说只要发送时指定了相同的Key,那么相关消息一定会发送到相同的Partition,Key一般而言都是字符串,最终都会被序列化为二进制。
- Value: 发送的具体内容,比如发送的消息是"你好",Value就是"你好",Value最终都会被序列化为二进制。
- Compression Type:压缩类型,其实就是压缩算法类型,这个字段决定了用哪种算法压缩Kafka消息,枚举值有none, gzip, lz4, snappy等。
- Headers:可以通过这个字段传递额外的Header,其实就是传递一些自定义的key-value对,比如想传递TraceID,就可以通过这个字段来进行。
- Partition + Offset:这个字段生产出来时候是空的,发送到Kafka的服务端后,会写入具体的分区的偏移,主题+分区+偏移其实就唯一对应了一条消息。
- Timestamp:时间戳,记录消息的时间。
3.1.2 消息发送的方式
- 同步发送:需要等待消息是否发送成功
- 发送即忘:发送之后就不管了
- 异步发送:发送之后等待返回结果(异步等待,不会阻塞主线程),可以设置回调函数(回调函数由sender线程执行,回调时会将sender线程激活,执行回调函数,异步执行)
四、消费者
- 不同消费者可以在同一时间对同一主题进行消费
- 相同消费者可以同一时间从同一主题的不同分片读取信息。
- 如果一个消费者,同时消费多个分片下,无法保证消息之间的先后顺序
- 如果一个消费者,只消费一个分片,消费顺序即生产顺序,符合队列的先入先出特性
kafka的消息是拉取模式
4.1 消费者offset
每条消息在Kafka中会有Partition ID以及OFFSET,通过这两个信息就可以定位到一条消息。消费者组消费消息之后会提交它在某个Partition对应的OFFSET,这样子下一次就可以从下个位置(OFFSET+1)开始消费。这个offset由broker维护。
4.2 主动提交和被动提交
- 自动提交(被动提交):拉取任务后就修改offset,但是可能消息失败,比如消费者在做完事情之前崩溃重启,那这条消息就丢了。
- 手动提交(主动提交):当某条消息的处理流程都ok了,再向Broker主动提交,这样更为稳健。一般是保证消息至少消费一次时使用
4.3 消费者组
-
同一消费者组可以消费不同的topic
-
消费者组包含多个消费者
-
topic中有多个partition
-
partition和消费者一对一
-
消费者组和topic多对多
4.3.1 消费者组分区分配策略
-
Range Assignor:基于范围的分配策略,将分区按照范围分配给消费者。
-
RoundRobin Assignor:基于轮询的分配策略,分区均匀地分配给消费者。
-
Sticky Assignor:优先保持当前的分配状态,并尽量减少在再平衡过程中的分区移动
-
CooperativeStickyAssignor:和Sticky Assignor的策略是基本一样的,区别在于该协议将原来的一次大的全部分区重平衡,改成多次小规模分区重平衡。简单理解就是渐进式重平衡。
4.4 消费组再平衡机制
触发再平衡时机
- 新消费者加入
- 有消费者退出
- 主题分区发生变化(增加分区)(分区不能减少,只能通过删除topic减少)
再平衡过程
- 暂停消费
- 触发再平衡
- 分配分区
- 消费者完成分工
- 恢复消费
再平衡模式
- Eager Rebalance:追求平衡,会导致所有消费者全部暂停消费
- Incremental Rebalance:只有部分消费者停止消费,但是可能会造成不均匀分配
协调器(Group Coordinator)
运行再broker服务器上(根据groupId决定放在哪个broker服务器上),每个消费者组都会有一个协调器,负责管理分配组内消费者和偏移量,以及接收消费者的心跳信息
Group Coordinator在收到消费者的加入请求后,会选择一个消费者作为Leader,这个Leader消费者会根据从Group Coordinator拿到的所有消费者信息,进行分配,并Group Coordinator发送SyncGroup请求,以完成分区分配。
五、实践经验
5.1 如何保证消息不丢失
成功发送
需要和客户端一起配合才能保证kafka成功接收到消息
配置策略:
- 无需响应
- 只需要leader响应
- 需要leader和其他broker都响应(leader是一个特殊的broker,类似于redis哨兵的leader)
存储持久
kafka一旦接收到消息就会进行持久化,保证存储持久
消费环节
使用偏移量记录消费位置,确保至少一次消费
5.2 如何保证消息不重复
不重复接收消息
判断消息id
Kafka是没办法解决重复消费的问题,依靠不重复消费信息
不重复消费消息
幂等消费解决:
- redis:
- 标识消息id,并使用分布式id
- 使用set,确保消息不重复接收
- 使用原子操作检查消息id,并确认是否添加
- mysql:
- 唯一约束
- insert ignor
- 事务
- 最后可以采用redis+mysql共同完成幂等消费
最后的幂等操作交给其他组件,kafka本身不保证幂等消费消息
消息接收保证成功一次:原子操作加反馈
消息接收保证不重复:不保证
消息消费保证成功一次:原子操作加反馈
消息消费保证不重复:交给redis或mysql
5.3 如何保证消息的有序性
- 每个业务只使用一个分区,使得消息本身便是有序的
- 分区:
- 子业务分区
- 客户分区
- 大小客户分区
核心就是一个partition下是有序的,尽量把没有顺序依赖的消息分到不同的分区,将需要有序的消息放在同一个分区中
5.4 如何消息不幸积压了,该如何处理
六、高可用
6.1 多副本机制

kafka中的副本就是分区的备份,主副本就是真正意义上正在使用的分区(图中的黄色部分),其他的副本存在与主副本不同的broker中,当作备份使用(分区的副本数量需要小于等于broker的数量)

为消费者位移主题建立副本(在配置中指定),可以使得消费偏移量不丢失
6.2 多副本机制下的写入操作
行为:(类似于mysql的主从同步机制)
这里的ALL并不是所有副本,而是在ISR机制中集合中的副本。
AR:所有的副本
ISR:基本和leader同步的副本
OSR:和leader同步差距较大的副本
AR = ISR + OSR
6.3 副本同步机制
同步流程:
LEO:每个副本都会维护一个自己的LEO,表示同步的偏移量,下次根据这个值,拉取消息进行同步
(同步时通过副本broker拉取leader broker的消息)
HW:先更新LEO,然后再更新HW,HW表示对外可见的消息(表示所有的副本都已经同步了该消息,具有可靠性)
Epoch机制优化同步消息:。。。。。。
七、高性能
7.1 查询数据流程
分片
topic ---> partition ---> 三个文件
- log文件:即消息本身
- index文件:类似于索引,辅助查询(结合offset)
- timeIndex文件:辅助查询
查询流程
- 找到topic
- 得到对应partition
- 根据offset,结合index、timeindex,进行二分查找,得到对应文件
- 查询log数据
7.2 顺序写
-
先写入os的page cache ,然后操作系统异步写入磁盘
(顺序写内存、顺序写磁盘)
-
将page cache当作缓存,如果命中就不需要查询磁盘了
7.3 零拷贝
使用零拷贝技术,加快读取磁盘发送到网络的速度
7.4 批量操作
批量生产
- 设置发送时长,达到时间才发送而不是每次一条消息就发送一次
- 设置发送大小,当发送内容达到大小的时候才发送
- 设置缓冲区
批量消费
- 设置消费者一次期望得到的消息数/消息数据大小
- 设置消费者一次最大可消费消息数
- 设置最长等待时间
7.5 数据压缩
将数据压缩,消费者消费的时候解压,以时间换空间