每个Broker启动时,都会创建对应分区状态机和副本状态机实例,但只有Controller所在的Broker才会调用startup方法启动它们,若Controller变更了,老的Controller需要调用状态机shutdown方法进行关闭。
1、ReplicaStateMachine
副本状态机,用于管理集群中副本的状态信息
副本状态
- NewReplica:副本被创建之后所处的状态。
- OnlineReplica:副本正常提供服务时所处的状态。
- OfflineReplica:副本服务下线时所处的状态。
- ReplicaDeletionStarted:副本被删除时所处的状态。
- ReplicaDeletionSuccessful:副本被成功删除后所处的状态。
- ReplicaDeletionIneligible:开启副本删除,但副本暂时无法被删除时所处的状态。
- NonExistentReplica:副本从副本状态机被移除前所处的状态。
流转规则
2、PartitionStateMachine
分区状态机保存了每个topic的分区状态及定义流转规则
分区状态
- NewPartition:代表分区刚被创建,并且包含了AR,但是此时Leader或者ISR还没有被创建。这种状态下不能竞选Leader
- OnlinePartition:代表分区的Leader已经被选举出来,并且此时已经产生了对应的ISR,分区能提供服务时的状态
- OfflinePartition:代表了分区的Leader由于某种原因下线时导致分区暂时不可用的状态。
- NonExistentPartition:代表分区被删除,并且从分区状态机移除后所处的状态
PartitionStateMachine的启动过程会初始化各个partition的状态
(初始化为三种状态:NewPartition、OnlinePartition、OfflinePartition),
- 首先会根据Leader Replica是否在线初始化为OnlinePartition或者OfflinePartition,
- 其次如果没有被分配Leader Replica,则初始化为NewPartition,
- 接着尝试将状态为OfflinePartition或者NewPartition的partition转换为OnlinePartition,
- 最后将Partition的状态通过ControllerChannelManager同步给其它剩余的Broker Server
执行分区状态转换的 handleStateChanges 方法,在方法调用时,不在【元数据缓存】中的所有分区的状态被初始化为NonExistentPartition
private def initializePartitionState() {
for((topicPartition, replicaAssignment) <- controllerContext.partitionReplicaAssignment) {
controllerContext.partitionLeadershipInfo.get(topicPartition) match {
case Some(currentLeaderIsrAndEpoch) =>
// partition已经被分配了Leader和ISR
controllerContext.liveBrokerIds.contains(currentLeaderIsrAndEpoch.leaderAndIsr.leader) match {
case true => // leader在线,状态为OnlinePartition
partitionState.put(topicPartition, OnlinePartition)
case false => //leader不在线,状态为OfflinePartition
partitionState.put(topicPartition, OfflinePartition)
}
//没有被分配Leader和ISR
case None =>
partitionState.put(topicPartition, NewPartition)
}
}
}
流转规则
原始状态 | 状态切换条件 | 切换状态 |
---|---|---|
NonExistentPartition | 监听到zk节点/brokers/topics/目录上的数据变化,读取对应的topic的信息、分区数、AR列表,并保存到Controller内存 | NewPartition |
NewPartition | 将AR列表中的第一个Live Brokers作为Leader,且Live Brokers作为ISR | OnlinePartition |
OfflinePartition | Leader Replica选举出Leader和ISR | OnlinePartition |
OnlinePartition | Leader Replica选举出Leader和ISR | OnlinePartition |
NewPartition | 无leader | OfflinePartition |
OnlinePartition | 无leader | OfflinePartition |
OfflinePartition | 无leader | OfflinePartition |
OfflinePartition | 主题被成功删除 | NonExistentPartition |
3、partition leader Election
只有Controller的broker才会触发Partition Leader Election
选举策略
当前 Kafka 有 4 种分区 Leader 选举策略。
- OfflinePartition Leader 选举
因为 Leader 副本下线而引发的分区。这是最常见的分区 Leader 选举场景
- ReassignPartition Leader 选举
手动运行 kafka-reassign-partitions 命令,或者调用Admin 的alterPartitionReassignments 方法执行分区副本重分配时
- PreferredReplicaPartition Leader 选举
为了避免分区副本分配不均匀,kafka引入了preferred副本概念,提供分区自动平衡解决分区不均匀问题。
- 设置broker端参数auto.leader.rebalance.enable为true(默认值),这样controller定时自动调整preferred leader
Kafka 的控制器会启动一个定时任务,这个定时任务会轮询所有的 broker 节点,计算每个 broker 节点的分区不平衡率(broker 中的不平衡率=非优先副本的 leader 个数/分区总数)是否超过 leader.imbalance.per.broker.percentage 参数配置的比值(默认值为10%),如果超过设定的比值则会自动执行优先副本的选举动作以求分区平衡。执行周期由参数 leader.imbalance.check.interval.seconds 控制,默认值为300秒
优先副本的自动选举执行时间不可控,可能引起负面的性能问题,所以实际生产上一般都会关闭参数,监控相关告警后进行人工触发
- 通过kafka-preferred-repica-election脚本手动触发。
- ControlledShutdownPartition Leader 选举
当 Broker 正常关闭时,该 Broker 上 的所有 Leader 副本都会下线,因此,需要为受影响的分区执行相应的 Leader选举
分区leader选举逻辑简而言之:
当前分区副本列表(AR)中首个存活且处于 ISR 列表中的副本作为 Leader 副本
OfflinePartition Leader 选举 会再次判断是否可进行Unclean Leader 选举:
如果没找到,则看是否允许Unclean Leader 选举, 如果不允许返回 None,表示没有选出 Leader;如果允许,则寻找分区副本列表中第一个存活的副本作为 Leader 副本