一、Kafka核心总控制器
定义:kafka集群中的一个负责管理所有分区和副本的状态的broker。
PS:kafka单台机器也叫集群。
职能:选举新的leader副本、ISR变更通知所有broker更新其元数据、让新分区被其他节点感知。
- 当某个分区的leader副本出现故障时,由控制器负责为该分区选举新的leader副本。
- 当检测到某个分区的ISR集合发生变化时,由控制器负责通知所有broker更新其元数据信息。
- 当使用kafka-topics.sh脚本为某个topic增加分区数量时,同样还是由控制器负责让新分区被其他节点感知到。
Controller选举原理
- kafka集群启动时,每个borker去ZK上创建一个“/controller 临时节点”,谁创建成功谁就是Controller。
- 当这个controller角色的broker宕机了,此时ZK上的临时节点会消失,集群里其他broker会一直监听这个临时节点,发现临时节点消失了,就竞争再次创建临时节点,ZK又会保证只有一个broker成为新的controller。
Partition副本选举Leader原理
- controller感知到分区leader所在的broker挂了,则会从ISR列表里挑第一个broker作为leader。
- 若参数unclean.leader.election.enable为true,代表在ISR列表里所有副本都挂了,则可以在ISR列表以外的副本中选leader。
副本进入ISR的条件
- 副本节点不能产生分区,必须能与zookeeper保持会话以及跟leader副本网络连通。
- 副本能复制leader上的所有写操作,并且不能落后太多。
PS:之所以取ISR中的第一个broker升级Leader,是因为第一个broker最先放进ISR 列表,可能是同步数据最多的副本。
二、消费者offset记录机制
offset记录机制
- 每个consumer会定期将自己消费分区的offset提交给kafka内部topic:__consumer_offsets。
- 提交过去的时候,key是consumerGroupId+topic+分区号,value就是当前offset的值。
- kafka会定期清理topic里的消息,最后就保留最新的那条数据。
PS:因为__consumer_offsets可能会接收高并发的请求,kafka默认给其分配50个分区(可以通过offsets.topic.num.partitions设置),这样可以通过加机器的方式抗大并发。
三、消费者Rebalance机制
Rebalance机制
定义:如果消费组里的消费者数量有变化或消费的分区数有变化,kafka会重新分配消费者和消费分区的关系。
PS:每个消费者都会有消费组,如果不指定会生成默认的组。
PS:rebalance只针对subscribe这种不指定分区消费的情况,如果通过assign这种消费方式指定了分区,kafka不会进行rebanlance。
PS:rebalance过程中,消费者无法从kafka消费消息!!!【尽量避免在系统高峰期时发生重平衡。】
触发场景:
- 1、消费组里的consumer增加或减少了
- 2、动态给topic增加了分区
- 3、消费组订阅了更多的topic
Rebalance过程
设计原理:分区方案制定过程中有两个组长,消费者组长负责制订分区策略,生产者组长负责通知其他消费者分区策略。【同一个消费者组中的消费者没有联系,需要通过kafka组长来协调。】
两个重要角色:
- 组协调器【生产者组长】:每个消费者组都会选择一个broker作为自己的组协调器coordinator,负责监控这个消费组里的所有消费者的心跳,以及判断是否宕机,然后开启消费者rebalance。
- 消费组协调器【消费者组长】:负责制定分区方案,并与组协调器进行通信。
PS:消费者组长不是我们所说的分区Leader!这个概念要区分好!
第一阶段:选择组协调器【生产者组长】
消费者组中的每个consumer启动时会向kafka集群中的某个节点发送查找组协调器的请求,并跟其建立网络连接。
组协调器选择方式
公式:hash(consumer_group_id) % _consumer_offsets主题的分区数。
根据公式获取到分区地址后,这个分区leader对应的broker就是这个消费者组的组协调器。
第二阶段:消费者入组
在成功找到消费组所对应的组协调器后就进入消费组入组的阶段,在此阶段的消费者会向组协调器发送入组请求。
组协调器从一个消费者组中选择第一个加入group的consumer作为消费者组长,把消费者组的情况发送给这个broker,接着这个broker会负责制定分区方案。
第三阶段:组协调器下发分区方案
消费者组长通过给组协调器发送下发分区策略请求,接着组协调器就把分区方案下发给各个consumer,他们会与指定分区的leader对应的broker进行网络连接以及消息消费。
Rebalance分区分配策略
- range【默认】:根据分区数/消费者数量,然后给每个消费者分配n个分区。【消费者1:0~3 消费者2:4~6 消费者3:7~9】
- round-robin:轮询分配机制。【消费者1:0、3、6 消费者2:1、4、7 消费者3:2、5、8】
- sticky:粘性轮询机制。【当分区增加或者增加消费者时,只会重新分配挂掉的那台或者新的分区。】
PS:range和轮询当分区增加或者增加消费者时,会重新轮询。而粘性策略则不会,粘性策略只会重新分配挂掉的那台或者新的分区。
四、生产者发布消息机制
写入方式
push模式将消息发布到broker,每条消息都被append到patition中,属于顺序写磁盘。
PS:顺序写磁盘效率比随机写内存要高,可以保障kafka吞吐量。
消息路由
producer发送消息到broker时,会根据分区算法选择将其存储到哪一个partition。其路由机制为:
- 1、指定了patition,则直接使用。
- 2、未指定patition但指定key,通过对key的value进行hash选出一个patition。【key的组成见本文上半部分的解析】
- 3、patition和key都未指定,使用轮询选出一个 patition。
写入流程
五、高水位
基本概念
- HW:高水位
- LEO:日志末端位移
HW即一个partition对应的ISR中最小的LEO(log-end-offset), consumer最多只能消费到HW所在的位置。
每个replica都有HW,leader和follower各自负责更新自己的HW的状态。
PS:对于leader新写入的消息,consumer不能立刻消费,leader会等待该消息被所有ISR中的replicas同步后更新HW,此时消息才能被consumer消费。
快乐图解~
六、日志分段存储原理
topic名称+分区号命名:
消息在分区内是分段(segment)存储,每个段的消息都存储在不一样的log文件里,这种特性方便old segment file快速被删除,kafka规定了一个段位的log文 件最大为1G,做这个限制目的是为了方便把 log 文件加载到内存去。
# 【部分消息的offset索引文件】,kafka每次往分区发4K(可配置)消息就会记录一条当前消息的offset到index文件
# 如果要定位消息的offset会先在这个文件里快速定位,再去log文件里找具体消息
00000000000000000000.index
# 【消息存储文件】,主要存offset和消息体
00000000000000000000.log
# 【消息的发送时间索引文件】,kafka每次往分区发4K(可配置)消息就会记录一条当前消息的发送时间戳与对应的offset到timeindex文件
# 如果需要按照时间来定位消息的offset,会先在这个文件里查找
00000000000000000000.timeindex
00000000000005367851.index
00000000000005367851.log
00000000000005367851.timeindex
00000000000009936472.index
00000000000009936472.log
00000000000009936472.timeindex
当储存满时每个log文件的大小是一样的,但是存储的message数量是不一定相等的(每条的message大小不一致)。
PS:每个日志段文件最大为1G。
- 文件的命名是以该segment最小offset来命名的,如00000000000000.index存储offset为0~5367850的消息...以此类推。
- kafka就是利用分段+索引的方式来解决查找效率的问题。