个人总结,请以官网为准
如有错误,欢迎指出
Leader的选举
Kafka Leader 选举分为三种
- broker Leader 选举
- 副本 Leader 选举
- 消费组 Leader 选举
Broker
一个Broker即可以理解为一台机器,Broker主要负责监控管理分区和副本的状态。在分区与副本的状态发生变化时,做出对应的操作。比如:分区的Leader副本出现了故障,那么broker需要进行Leader 副本的选举。Broker中的Leader Broker 称为Controller控制器。
选举过程
broker们会向zookeeper进行节点/controller的创建,谁先创建成功,谁就是Leader.并将自己的brokerId写入节点的值中。如果其他的broker发现节点的值不为-1,则放弃选举成为follower.
防止脑裂:
当旧的Leader重生后,集群中的新的Leader已经存在了,这时zookeeper就不知道听谁的话了。
这就出现一个集群有两个大脑的情况了,即脑裂。
broker在创建控制器节点的时候,还会创建一个/Controller epoch,每当新的节点成为控制器,那么就自动加一。这样就防止旧的Leader影响,如果它给Zookeeper的值小于zookeeper 中epoch的值,直接拒绝它成为Leader
Controller具体区别于其他节点的工作
责任越大,能力越大。所以来看看,Controller需要一些什么额外工作
- 监听分区的变化,做一些比如:副本选举的工具,重分区的工作
- 监听主题的变化
- 监听broker的变化
- auto.leader.rebalance.enble开启的情况下,会启动定时任务进行优先副本的均衡
Controller需要监听Zookeeper上数据变化,并同步给其他的节点,这些数据大概是:分区数变化,新的Leader副本等之类。
在整个Kafka的运行过程中一定会涉及到各种各样的的事件的产生然后触发节点间的通讯。这个过程,通过一个LinkBlockQuene,然后使用一个专有的线程去处理。避免使用锁机制降低效率
如图:
在早期版本中,并没有Controller这个中间商,所有的节点都需要去订阅Zookeeper的事件,如果加分区了,一定要惊动所有人的事件,却要惊动所有节点。这就造成了羊群效应
分区副本Leader选举
那些情况会触发:
- 分区副本创建或者分区上下线的时候
- 分区副本重新分配,因为需要保持一定负载均衡(分区的均衡,数据的均衡保证不了)
- Broker节点宕机
总的来说,从AR中选取第一个存活的节点分区,并且它在ISR中的。
为什么是AR,不是ISR
AR在不发生分区重分配的情况下,一般分区的顺序是不变的,而ISR会由于数据同步延迟的问题,一直在变化。
优先副本解决Leader分配不均衡的问题
在整个Kafka的长期运行中,会存在Leader副本的选举,挂掉,然后重新选举的情况。Kafka是不支持读写分离的,那么只有Leader副本在处理读写,那么如果在这个过程中出现了,多个分区的副本Leader到了同一台Broker上,那么分区的负载均衡的意义就失去了。所以Kafka,引入优先副本的概念。
优先副本即为AR的第一个副本,auto.leader.rebalance.enble开启的情况下,Controller会启动定时任务进行优先副本的均衡,但是在生产环境中建议关闭。这样减少这个过程的对消费者的影响。
通过Kafka自带的一个脚本完成,列如:
首先看到这个Topic的分区1,2的副本Leader都在 0 这台Broker上,通过执行下面这个脚本我们再看分区情况,它就会默认为每一个分区的AR副本中第一个。
我思考过一个问题,这样就一定能负载均衡吗?
我没有得到明确的答案,只是看到书里写这样一句话:Kafka会保证副本的尽量均匀的分布,并且同一个分区的两个副本一定不会出现在同一个Broker上。
所以基于上面的原因,再使用以上的分区机制是可以做到分区的负载均衡的,注意:是分区的负载均衡,不是数据的,如果你的数据分布的不均匀的话,消费者端也是不能负载均衡的。
注意:在节点挂掉后,节点上的副本数据是不会自动同步到其他节点的,这需要我们自己通过Kafka自带的脚本,Kafka-reassign-partition.sh去完成。
消费组 Leader 选举
基本上就是随机了,就是这么的任性与随意。源码中是这样的从一个hash列表中取一个消费者作为Leader。
我们知道hash是没有顺序的,那么就是随机了
leader的选举是因为某一个消费者Leader下线了,这时候就出现同一组中消费者数量的变化。这就不得不提到消费组的再均衡。由于一些原因,分区没有人消费了,那么就需要将它们分配给新的消费者或者存活下来的消费,否则业务逻辑就会出现漏数的情况了。这个再均衡的过程会引起Stop the world的情况,所有的消费者都不能消费了。这是很可怕的情况,所有避免发生或者避免发生的时段。
什么情况下会发生?
- 同一组中消费者数量的变化,有进有出
- 主题的分区数的变化,加减分区
- 组协调者节点的下线更换
分区的分配策略
RangeAssignor:
- 针对一个主题下的消费者和分区按字典排序,然后partition size/consumer size=m 并且
partition size%consumer size=n,那么前n个消费者分配到m+1,其他消费者分配到m个。
RoundRobinAssignor(轮询):
将消费组内部的所有消费者和所有主题的分区按字典顺序排序,然后将分区依次分给消费者。
如下图t0表示topic0,p1表示partition 1:
StickyAssignor
在初始的情况下,他与轮询策略的结果是一样,但是当出现重新分配的时候。它会在尽可能保证分区不变化位置的情况,保证分区的均衡。
- 分区位置尽可能不变化
- 保证最终结果均衡
思考:为什么Kafka不支持主从读写
首先第一个问题,主从读写解决了什么问题。
解决了节点压力的问题,主节点写数据,从节点读数据。但是Kafka利用partition的方式,已经做到将同一个topic的数据分散来节点的压力。
然后主从读写带来了什么问题
主从的模式带来的数据的延迟,从节点总是会落后主节点数据ms级别,甚至秒级别。但是在kafka除了在传统的程序中做削峰,异步的中间件外。它还是流式程序中中间件,比如Flink,SparkStreaming,Spark的实时性其实不高,它是一批次一批次处理,减少批次之间的间隔来完成假的实时的功能。但是Flink就真的是实时,来一条干一条。那么在实时性高的场景中,如果出现秒级别甚至由于网络的原因,出现了分区级别的延迟,在实时程序中是不允许的。
ISR
消息从生产者写入kafka,首先写入副本Leader,然后副本同步Leader的消息。(消息确认机制)
然后在同步消息落后的副本会被踢出ISR.
所以ISR的概念是,能追赶的上Leader的所有副本。
那些情况会落后于Leader
- 副本是新加入的,直到它同步与Leader一致才能加入ISR
- 副本卡死,副本机器死亡或者GC卡死
- 副本同步有与网络或其他原因,延迟
怎么判断副本落后了
replica.lag.time.max.ms 参数:如果副本落后超过这个时间就判定为落后了,直到它回来
消息复制分为异步和同步(这就与ACK的配置有关),ISR是一直动态有进出的