你想知道的所有关于Kafka Leader选举流程和选举策略都在这(内含12张高清图)

言尽于此,完结

无论是一个初级的 coder,高级的程序员,还是顶级的系统架构师,应该都有深刻的领会到设计模式的重要性。

  • 第一,设计模式能让专业人之间交流方便,如下:

程序员A:这里我用了XXX设计模式

程序员B:那我大致了解你程序的设计思路了

  • 第二,易维护

项目经理:今天客户有这样一个需求…

程序员:明白了,这里我使用了XXX设计模式,所以改起来很快

  • 第三,设计模式是编程经验的总结

程序员A:B,你怎么想到要这样去构建你的代码

程序员B:在我学习了XXX设计模式之后,好像自然而然就感觉这样写能避免一些问题

  • 第四,学习设计模式并不是必须的

程序员A:B,你这段代码使用的是XXX设计模式对吗?

程序员B:不好意思,我没有学习过设计模式,但是我的经验告诉我是这样写的

image

从设计思想解读开源框架,一步一步到Spring、Spring5、SpringMVC、MyBatis等源码解读,我都已收集整理全套,篇幅有限,这块只是详细的解说了23种设计模式,整理的文件如下图一览无余!

image

搜集费时费力,能看到此处的都是真爱!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

在开始源码分析之前, 大家先看下面这张图, 好让自己对Leader选举有一个非常清晰的认知,然后再去看后面的源码分析文章,会更容易理解。

点击阅读原文查看高清大图

整个流程分为三大块

  1. 触发选举场景 图左

  2. 执行选举流程 图中

  3. Leader选举策略 图右

分区状态机

首先大家得了解两个状态机

1. 分区状态机 控制分区状态流转

2. 副本状态机 控制副本状态流转

这里我们主要讲解分区状态机,这张图表示的是分区状态机

分区状态机 (点击阅读原文查看高清大图)

  1. NonExistentPartition :分区在将要被创建之前的初始状态是这个,表示不存在

  2. NewPartition: 表示正在创建新的分区, 是一个中间状态, 这个时候只是在Controller的内存中存了状态信息

  3. OnlinePartition: 在线状态, 正常的分区就应该是这种状态,只有在线的分区才能够提供服务

  4. OfflinePartition: 下线状态, 分区可能因为Broker宕机或者删除Topic等原因流转到这个状态, 下线了就不能提供服务了

  5. NonExistentPartition: 分区不存在的状态, 当Topic删除完成成功之后, 就会流转到这个状态, 当还处在删除中的时候,还是停留在下线状态。

我们今天要讲的Leader选举

就是在之前状态=>OnlinePartition状态的时候发生的。

Leader选举流程分析

源码入口:

PartitionStateMachine#electLeaderForPartitions

/**

  • 状态机流转 处理

**/

private def doHandleStateChanges(

partitions: Seq[TopicPartition],

targetState: PartitionState,

partitionLeaderElectionStrategyOpt: Option[PartitionLeaderElectionStrategy]

): Map[TopicPartition, Either[Throwable, LeaderAndIsr]] = {

//不相干的代码省略了

targetState match {

// 不相干的代码省略了, 这里状态流程到 OnlinePartition

case OnlinePartition =>

// 分区状态是 OfflinePartition 或者 OnlinePartition 的话 就都需要执行一下选举策略

if (partitionsToElectLeader.nonEmpty) {

// 根据选举策略 进行选举。这里只是找出

val electionResults = electLeaderForPartitions(

partitionsToElectLeader,

partitionLeaderElectionStrategyOpt.getOrElse(

throw new IllegalArgumentException(“Election strategy is a required field when the target state is OnlinePartition”)

)

)

}

}

可以看到 我们最终是调用了doElectLeaderForPartitions 执行分区Leader选举。

PartitionStateMachine#doElectLeaderForPartitions

// 删除了部分无关代码

private def doElectLeaderForPartitions(

partitions: Seq[TopicPartition],

partitionLeaderElectionStrategy: PartitionLeaderElectionStrategy

): (Map[TopicPartition, Either[Exception, LeaderAndIsr]], Seq[TopicPartition]) = {

// 去zookeeper节点 /broker/topics/{topic名称}/partitions/{分区号}/state 节点读取基本信息。

val getDataResponses = try {

zkClient.getTopicPartitionStatesRaw(partitions)

}

val failedElections = mutable.Map.empty[TopicPartition, Either[Exception, LeaderAndIsr]]

val validLeaderAndIsrs = mutable.Buffer.empty[(TopicPartition, LeaderAndIsr)]

// 遍历从zk中获取的数据返回信息

getDataResponses.foreach { getDataResponse =>

val partition = getDataResponse.ctx.get.asInstanceOf[TopicPartition]

// 当前分区状态

val currState = partitionState(partition)

if (getDataResponse.resultCode == Code.OK) {

TopicPartitionStateZNode.decode(getDataResponse.data, getDataResponse.stat) match {

case Some(leaderIsrAndControllerEpoch) =>

if (leaderIsrAndControllerEpoch.controllerEpoch > controllerContext.epoch) {

//…

} else {

// 把通过校验的leaderandisr信息 保存到列表

validLeaderAndIsrs += partition -> leaderIsrAndControllerEpoch.leaderAndIsr

}

case None =>

//…

}

} else if (getDataResponse.resultCode == Code.NONODE) {

//…

} else {

//…

}

}

// 如果没有 有效的分区,则直接返回

if (validLeaderAndIsrs.isEmpty) {

return (failedElections.toMap, Seq.empty)

}

// 根据入参 传入的 选举策略 来选择Leader

val (partitionsWithoutLeaders, partitionsWithLeaders) = partitionLeaderElectionStrategy match {

// 离线分区 策略 (allowUnclean 表示的是是否允许脏副本参与选举, 如果这里是true,则忽略topic本身的unclean.leader.election.enable 配置,如果是false,则会考虑 unclean.leader.election.enable 的配置。 )

case OfflinePartitionLeaderElectionStrategy(allowUnclean) =>

// 这里就是判断allowUnclean的参数,如果这里是true,则忽略topic本身的unclean.leader.election.enable 配置,如果是false,则会考虑 unclean.leader.election.enable 的配置。因为每个topic的配置可能不一样,所以这里组装每个分区的信息和allowUnclean 返回

val partitionsWithUncleanLeaderElectionState = collectUncleanLeaderElectionState(

validLeaderAndIsrs,

allowUnclean

)

// 去选择一个合适的副本 来当选 leader。这里只是计算得到了一个值们还没有真的当选哈

leaderForOffline(controllerContext, partitionsWithUncleanLeaderElectionState).partition(_.leaderAndIsr.isEmpty)

// 分区副本重分配Leader 选举策略

case ReassignPartitionLeaderElectionStrategy =>

// 去选择一个合适的副本 来当选 leader。这里只是计算得到了一个值们还没有真的当选哈

leaderForReassign(controllerContext, validLeaderAndIsrs).partition(_.leaderAndIsr.isEmpty)

// 优先副本选举策略

case PreferredReplicaPartitionLeaderElectionStrategy =>

// 去选择一个合适的副本 来当选 leader。这里只是计算得到了一个值们还没有真的当选哈

leaderForPreferredReplica(controllerContext, validLeaderAndIsrs).partition(_.leaderAndIsr.isEmpty)

// 受控关机策略

case ControlledShutdownPartitionLeaderElectionStrategy =>

// 去选择一个合适的副本 来当选 leader。这里只是计算得到了一个值们还没有真的当选哈

leaderForControlledShutdown(controllerContext, validLeaderAndIsrs).partition(_.leaderAndIsr.isEmpty)

}

// 这里是上面策略 没有找到Leader的所有分区,遍历一下,打一个异常日志。

partitionsWithoutLeaders.foreach { electionResult =>

val partition = electionResult.topicPartition

val failMsg = s"Failed to elect leader for partition $partition under strategy $partitionLeaderElectionStrategy"

failedElections.put(partition, Left(new StateChangeFailedException(failMsg)))

}

// 整理一下上面计算得到哦的结果

val recipientsPerPartition = partitionsWithLeaders.map(result => result.topicPartition -> result.liveReplicas).toMap

val adjustedLeaderAndIsrs = partitionsWithLeaders.map(result => result.topicPartition -> result.leaderAndIsr.get).toMap

// 这里去把leader和isr的信息写入到zk中去啦 节点 /broker/topics/{topic名称}/partitions/{分区号}/state

val UpdateLeaderAndIsrResult(finishedUpdates, updatesToRetry) = zkClient.updateLeaderAndIsr(

adjustedLeaderAndIsrs, controllerContext.epoch, controllerContext.epochZkVersion)

// 遍历更新完成的分区, 然后更新Controller里面的分区leader和isr的内存信息 并发送LeaderAndISR请求

finishedUpdates.foreach { case (partition, result) =>

result.right.foreach { leaderAndIsr =>

val replicaAssignment = controllerContext.partitionFullReplicaAssignment(partition)

val leaderIsrAndControllerEpoch = LeaderIsrAndControllerEpoch(leaderAndIsr, controllerContext.epoch)

// 更新内存

controllerContext.partitionLeadershipInfo.put(partition, leaderIsrAndControllerEpoch)

// 发送LeaderAndIsr请求

controllerBrokerRequestBatch.addLeaderAndIsrRequestForBrokers(recipientsPerPartition(partition), partition,

leaderIsrAndControllerEpoch, replicaAssignment, isNew = false)

}

}

(finishedUpdates ++ failedElections, updatesToRetry)

}

总结一下上面的源码

  1. 去zookeeper节点/broker/topics/{topic名称}/partitions/{分区号}/state 节点读取基本信息。

  2. 遍历从zk中获取的leaderIsrAndControllerEpoch信息,做一些简单的校验:zk中获取的数据的controllerEpoch必须<=当前的Controller的controller_epoch。最终得到 validLeaderAndIsrs, controller_epoch 就是用来防止脑裂的, 当有两个Controller当选的时候,他们的epoch肯定不一样, 那么最新的epoch才是真的Controller

  3. 如果没有获取到有效的validLeaderAndIsrs 信息 则直接返回

  4. 根据入参partitionLeaderElectionStrategy 来匹配不同的Leader选举策略。来选出合适的Leader和ISR信息

  5. 根据上面的选举策略选出的 LeaderAndIsr 信息进行遍历, 将它们一个个写入到zookeeper节点/broker/topics/{topic名称}/partitions/{分区号}/state中。 (当然如果上面没有选择出合适的leader,那么久不会有这个过程了)

  6. 遍历上面写入zk成功的分区, 然后更新Controller里面的分区leader和isr的内存信息 并发送LeaderAndISR请求,通知对应的Broker Leader更新了。

Leader选举流程 (点击阅读原文查看高清大图)

看上面的Leader选举策略是不是很简单, 但是中间究竟是如何选择Leader的?

这个是根据传入的策略类型, 来做不同的选择

那么有哪些策略呢?以及什么时候触发这些选举呢?

分区的几种策略以及对应的触发场景


1. OfflinePartitionLeaderElectionStrategy

遍历分区的AR, 找到第一个满足以下条件的副本:

  1. 副本在线
  1. 在ISR中

如果找不到满足条件的副本,那么再根据 传入的参数allowUnclean判断

  1. allowUnclean=true:AR顺序中所有在线副本中的第一个副本。
  1. allowUnclean=false: 需要去查询配置 unclean.leader.election.enable 的值。
若=true ,则跟上面 1一样 。  
若=false,直接返回None,没有找到合适的Leader。

离线分区Leader选举策略 (点击阅读原文查看高清大图)

源码位置:

Election#leaderForOffline

case OfflinePartitionLeaderElectionStrategy(allowUnclean) =>

// 这里是组装所有分区的信息啊, 返回的对象是 1. 分区 2. leader、isr and controller epoc 3. allow unclean 是否允许脏副本参与竞选

val partitionsWithUncleanLeaderElectionState = collectUncleanLeaderElectionState(

validLeaderAndIsrs,

allowUnclean

)

// 调用leader选举

leaderForOffline(controllerContext, partitionsWithUncleanLeaderElectionState).partition(_.leaderAndIsr.isEmpty)

private def leaderForOffline(partition: TopicPartition,

leaderAndIsrOpt: Option[LeaderAndIsr],

uncleanLeaderElectionEnabled: Boolean,

controllerContext: ControllerContext): ElectionResult = {

// 当前分区的AR

val assignment = controllerContext.partitionReplicaAssignment(partition)

// 所有在线的副本

val liveReplicas = assignment.filter(replica => controllerContext.isReplicaOnline(replica, partition))

leaderAndIsrOpt match {

case Some(leaderAndIsr) =>

val isr = leaderAndIsr.isr

// 找到 第一个满足条件:副本在线 && 在 ISR中的副本。 如果没有满足条件的 则判断入参uncleanLeaderElectionEnabled的配置

// 如果是true,则从不在isr中的存活副本中获取副本作为leader

val leaderOpt = PartitionLeaderElectionAlgorithms.offlinePartitionLeaderElection(

assignment, isr, liveReplicas.toSet, uncleanLeaderElectionEnabled, controllerContext)

val newLeaderAndIsrOpt = leaderOpt.map { leader =>

val newIsr = if (isr.contains(leader)) isr.filter(replica => controllerContext.isReplicaOnline(replica, partition))

else List(leader)

leaderAndIsr.newLeaderAndIsr(leader, newIsr)

}

ElectionResult(partition, newLeaderAndIsrOpt, liveReplicas)

case None =>

ElectionResult(partition, None, liveReplicas)

}

}

// 找到 第一个满足条件:副本在线 && 在 ISR中的副本。 如果没有满足条件的 则判断入参allowUnclean的配置,如果是true,则从不在isr中的存活副本中获取副本作为leader

object PartitionLeaderElectionAlgorithms {

def offlinePartitionLeaderElection(assignment: Seq[Int], isr: Seq[Int], liveReplicas: Set[Int], uncleanLeaderElectionEnabled: Boolean, controllerContext: ControllerContext): Option[Int] = {

assignment.find(id => liveReplicas.contains(id) && isr.contains(id)).orElse {

if (uncleanLeaderElectionEnabled) {

val leaderOpt = assignment.find(liveReplicas.contains)

if (leaderOpt.isDefined)

controllerContext.stats.uncleanLeaderElectionRate.mark()

leaderOpt

} else {

None

}

}

}

  1. 先组装所有给定的 validLeaderAndIsrs 的信息

其实主要还是要去获取每个Topic的对应的unclean.leader.election.enable 属性值。

默认情况下,我们调用到这里的时候 这个入参allowUnclean=false.

如果是false 那我们需要去查询一下指定的topic它的属性unclean.leader.election.enable 是什么

如果是true 则表示直接覆盖了unclean.leader.election.enable的配置为true。

在这里插入图片描述

  1. 找到 第一个满足条件:副本在线 && 在 ISR中的副本

  2. 如果没有满足条件的 则判断入uncleanLeaderElectionEnabled的配置

如果是true,则从不在isr中的存活副本中获取副本作为leader。

当然这个uncleanLeaderElectionEnabled 参数是上 步骤1中决定的。

触发场景:Controller 重新加载

Controller 当选的时候会启动 分区状态机 partitionStateMachine, 启动的时候会重新加载所有分区的状态到内存中, 并触发 对处于 NewPartitionOfflinePartition 状态的所有分区尝试变更为 OnlinePartition 状态的状态。把新创建的分区和离线的分区触发一下选举流程啊

触发源码入口:

KafkaController#onControllerFailover

partitionStateMachine.startup()

partitionStateMachine.triggerOnlinePartitionStateChange()

加szzdzhp001,领取全部kafka知识图谱

触发场景:脚本执行脏选举

当执行 kafka-leader-election.sh 的时候并且 模式选择的是UNCLEAN . 则会触发这个模式。

这里注意一下,入参allowUnclean = (electionTrigger == AdminClientTriggered)

意思是: 当触发的场景是AdminClientTriggered的时候, 则allowUnclean=true,表示 不关心配置参数 unclean.leader.election.enable 是什么, 如果没有找到符合条件的Leader, 则就去非ISR 列表找Leader。

刚好 我能脚本执行的时候 触发器就是 AdminClientTriggered

其他触发器有:

AutoTriggered : 定时自动触发。

ZkTriggered:Controller切换的时候触发的(zk节点/controller 的变更便是Controller角色的切换)

AdminClientTriggered:客户端主动触发。

触发场景:Controller 监听到有Broker启动了

同上。

触发源码入口:

KafkaController#processBrokerChange#onBrokerStartup

partitionStateMachine.triggerOnlinePartitionStateChange()

触发场景:Controller 监听 LeaderAndIsrResponseReceived请求

同上。

当Controller向对应的Broker发起 LeaderAndIsrRequest 请求的时候.

有一个回调函数callback, 这个回调函数会向Controller发起一个事件为 LeaderAndIsrResponseReceived 请求。

具体源码在:

ControllerChannelManager#sendLeaderAndIsrRequest

在这里插入图片描述

Controller收到这个事件的请求之后,根据返回的 leaderAndIsrResponse 数据

会判断一下有没有新增加的离线副本(一般都是由于磁盘访问有问题)

如果有新的离线副本,则需要将这个离线副本标记为Offline状态

源码入口:

KafkaController#onReplicasBecomeOffline

partitionStateMachine.triggerOnlinePartitionStateChange()

加szzdzhp001,领取全部kafka知识图谱

触发场景:Controller 监听 UncleanLeaderElectionEnable请求

当我们在修改动态配置的时候, 将动态配置:unclean.leader.election.enable设置为 true 的时候

会触发向Controller发起UncleanLeaderElectionEnable的请求,这个时候则需要触发一下。触发请求同上

触发源码入口:

KafkaController#processTopicUncleanLeaderElectionEnable

partitionStateMachine.triggerOnlinePartitionStateChange(topic)

上面的触发调用的代码就是下面的接口

对处于 NewPartitionOfflinePartition 状态的所有分区尝试变更为

OnlinePartition 的状态。 状态的流程触发了Leader选举。

/**

  • 此 API 对处于 NewPartition 或 OfflinePartition 状态的所有分区尝试变更为

  • OnlinePartition 状态的状态。 这在成功的控制器选举和代理更改时调用

*/

def triggerOnlinePartitionStateChange(): Unit = {

// 获取所有 OfflinePartition 、NewPartition 的分区状态

val partitions = controllerContext.partitionsInStates(Set(OfflinePartition, NewPartition))

triggerOnlineStateChangeForPartitions(partitions)

}

private def triggerOnlineStateChangeForPartitions(partitions: collection.Set[TopicPartition]): Unit = {

// 尝试将 所有 NewPartition or OfflinePartition 状态的分区全部转别成 OnlinePartition状态,

//但是除了那个分区所对应的Topic正在被删除的所有分区

val partitionsToTrigger = partitions.filter { partition =>

!controllerContext.isTopicQueuedUpForDeletion(partition.topic)

}.toSeq

// 分区状态机进行状态流转 使用 OfflinePartitionLeaderElectionStrategy 选举策略(allowUnclean =false 不允许 不在isr中的副本参与选举)

handleStateChanges(partitionsToTrigger, OnlinePartition, Some(OfflinePartitionLeaderElectionStrategy(false)))

}

  1. 获取所有 OfflinePartition 、NewPartition 的分区状态

  2. 尝试将 所有 NewPartition or OfflinePartition 状态的分区全部转别成 OnlinePartition状态,

但是如果对应的Topic正在删除中,则会被排除掉

  1. 分区状态机进行状态流转 使用 OfflinePartitionLeaderElectionStrategy 选举策略(allowUnclean=true 表示如果从isr中没有选出leader,则允许从非isr列表中选举leader ,allowUnclean=false 表示如果从isr中没有选出leader, 则需要去读取配置文件的配置 unclean.leader.election.enable 来决定是否允许从非ISR列表中选举Leader。 )

加szzdzhp001,领取全部kafka知识图谱

2. ReassignPartitionLeaderElectionStrategy

分区副本重分配选举策略:

当执行分区副本重分配的时候, 原来的Leader可能有变更, 则需要触发一下 Leader选举。

  1. 只有当之前的Leader副本在经过重分配之后不存在了
例如: \[2,0\] ==> \[1,0\] 。 原来2是Leader副本,经过重分配之后变成了 \[1,0\]。2已经不复存在了,所以需要重新选举Leader。
  1. 当原来的分区Leader副本 因为某些异常,下线了。需要重新选举Leader

分区副本重分配发生的Leader选举.

Election#leaderForReassign

private def leaderForReassign(partition: TopicPartition,

leaderAndIsr: LeaderAndIsr,

controllerContext: ControllerContext): ElectionResult = {

// 从Controller的内存中获取当前分区的分配情况, 然后跟 removingReplicas(表示当前重分配需要移除掉的副本) 取差集。也就获取当重分配之后剩下的所有副本分配情况了。

val targetReplicas = controllerContext.partitionFullReplicaAssignment(partition).targetReplicas

// 过滤一下不在线的副本。

val liveReplicas = targetReplicas.filter(replica => controllerContext.isReplicaOnline(replica, partition))

// 这里的isr 是从外部传参进来的, 是去zk节点 /brokers/topics/{topic名称}/partitions/{分区号}/state 中拿取的数据,而不是当前内存中拿到的

val isr = leaderAndIsr.isr

// 在上面的targetReplicas中找到符合条件的第一个元素:副本必须在线, 副本必须在ISR中。

val leaderOpt = PartitionLeaderElectionAlgorithms.reassignPartitionLeaderElection(targetReplicas, isr, liveReplicas.toSet)

// 构造一下 上面拿到的Leader参数, 组装成一个LeaderAndIsr对象,对象多组装了例如:leaderEpoch+1, zkVersion 等等

val newLeaderAndIsrOpt = leaderOpt.map(leader => leaderAndIsr.newLeader(leader))

ElectionResult(partition, newLeaderAndIsrOpt, targetReplicas)

}

// 这个算法就是找到 第一个 符合条件:副本在线,副本在ISR中 的副本。用于遍历的reassignment就是我们上面的targetReplicas,是从内存中获取的。也就是变更后的副本顺序了。那么就是获取了第一个副本啦

def reassignPartitionLeaderElection(reassignment: Seq[Int], isr: Seq[Int], liveReplicas: Set[Int]): Option[Int] = {

reassignment.find(id => liveReplicas.contains(id) && isr.contains(id))

}

总结:

从当前的副本分配列表中,获取副本在线&&副本在ISR中的 第一个副本,遍历的顺序是当前副本的分配方式(AR),跟ISR的顺序没有什么关系。

加szzdzhp001,领取全部kafka知识图谱

触发场景:分区副本重分配

并不是每次执行分区副本重分配都会触发这个Leader选举策略, 下面两种情况才会触发

  1. 只有当之前的Leader副本在经过重分配之后不存在了。例如: [2,0] ==> [1,0] 。 原来2是Leader副本,经过重分配之后变成了 [1,0]。2已经不复存在了,所以需要重新选举Leader。
  1. 当原来的分区Leader副本 因为某些异常,下线了。需要重新选举Leader

对应的判断条件代码如下:

KafkaController#moveReassignedPartitionLeaderIfRequired

private def moveReassignedPartitionLeaderIfRequired(topicPartition: TopicPartition,

newAssignment: ReplicaAssignment): Unit = {

// 重分配之后的所有副本

val reassignedReplicas = newAssignment.replicas

//当前的分区Leader是哪个

val currentLeader = controllerContext.partitionLeadershipInfo(topicPartition).leaderAndIsr.leader

// 如果分配后的副本不包含当前Leader副本,则需要重新选举

if (!reassignedReplicas.contains(currentLeader)) {

//触发Leader重选举,策略是ReassignPartitionLeaderElectionStrategy

partitionStateMachine.handleStateChanges(Seq(topicPartition), OnlinePartition, Some(ReassignPartitionLeaderElectionStrategy))

} else if (controllerContext.isReplicaOnline(currentLeader, topicPartition)) {

// 上面2种情况都不符合, 那么就没有必要leader重选举了, 更新一下leaderEpoch就行 了

updateLeaderEpochAndSendRequest(topicPartition, newAssignment)

} else {

//触发Leader重选举,策略是ReassignPartitionLeaderElectionStrategy

partitionStateMachine.handleStateChanges(Seq(topicPartition), OnlinePartition, Some(ReassignPartitionLeaderElectionStrategy))

}

}

在这里插入图片描述

点击查看分区重分配的源码解析

3. PreferredReplicaPartitionLeaderElectionStrategy

优先副本选举策略, 必须满足三个条件:

是第一个副本&&副本在线&&副本在ISR列表中。

满足上面三个条件才会当选leader,不满足则不会做变更。

优先副本选举 (点击阅读原文看高清大图)

def leaderForPreferredReplica(controllerContext: ControllerContext,

leaderAndIsrs: Seq[(TopicPartition, LeaderAndIsr)]): Seq[ElectionResult] = {

leaderAndIsrs.map { case (partition, leaderAndIsr) =>

leaderForPreferredReplica(partition, leaderAndIsr, controllerContext)

}

}

private def leaderForPreferredReplica(partition: TopicPartition,

leaderAndIsr: LeaderAndIsr,

controllerContext: ControllerContext): ElectionResult = {

// AR列表

val assignment = controllerContext.partitionReplicaAssignment(partition)

// 在线副本

val liveReplicas = assignment.filter(replica => controllerContext.isReplicaOnline(replica, partition))

val isr = leaderAndIsr.isr

// 找出第一个副本 是否在线 并且在ISR中。

val leaderOpt = PartitionLeaderElectionAlgorithms.preferredReplicaPartitionLeaderElection(assignment, isr, liveReplicas.toSet)

// 组装leaderandisr返回 ,注意这里是没有修改ISR信息的

val newLeaderAndIsrOpt = leaderOpt.map(leader => leaderAndIsr.newLeader(leader))

ElectionResult(partition, newLeaderAndIsrOpt, assignment)

}

def preferredReplicaPartitionLeaderElection(assignment: Seq[Int], isr: Seq[Int], liveReplicas: Set[Int]): Option[Int] = {

assignment.headOption.filter(id => liveReplicas.contains(id) && isr.contains(id))

}

  1. 从内存中获取TopicPartition的分配方式

  2. 过滤不在线的副本

  3. 找到第一个副本判断一下是否在线&&在ISR列表中。如果满足,则选他为leader,如果不满足,也不会再找其他副本了。

  4. 返回leaderAndIsr信息, 这里的ISR是没有做修改的。

加szzdzhp001,领取全部kafka知识图谱

触发场景:自动定时执行优先副本选举任务

Controller 启动的时候,会启动一个定时任务 。每隔一段时间就去执行 优先副本选举任务。

与之相关配置:

如果为true表示会创建定时任务去执行 优先副本选举,为false则不会创建

auto.leader.rebalance.enable=true

每隔多久执行一次 ; 默认300秒;

leader.imbalance.check.interval.seconds partition = 300

总结

这份面试题几乎包含了他在一年内遇到的所有面试题以及答案,甚至包括面试中的细节对话以及语录,可谓是细节到极致,甚至简历优化和怎么投简历更容易得到面试机会也包括在内!也包括教你怎么去获得一些大厂,比如阿里,腾讯的内推名额!

某位名人说过成功是靠99%的汗水和1%的机遇得到的,而你想获得那1%的机遇你首先就得付出99%的汗水!你只有朝着你的目标一步一步坚持不懈的走下去你才能有机会获得成功!

成功只会留给那些有准备的人!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

erElection(assignment: Seq[Int], isr: Seq[Int], liveReplicas: Set[Int]): Option[Int] = {

assignment.headOption.filter(id => liveReplicas.contains(id) && isr.contains(id))

}

  1. 从内存中获取TopicPartition的分配方式

  2. 过滤不在线的副本

  3. 找到第一个副本判断一下是否在线&&在ISR列表中。如果满足,则选他为leader,如果不满足,也不会再找其他副本了。

  4. 返回leaderAndIsr信息, 这里的ISR是没有做修改的。

加szzdzhp001,领取全部kafka知识图谱

触发场景:自动定时执行优先副本选举任务

Controller 启动的时候,会启动一个定时任务 。每隔一段时间就去执行 优先副本选举任务。

与之相关配置:

如果为true表示会创建定时任务去执行 优先副本选举,为false则不会创建

auto.leader.rebalance.enable=true

每隔多久执行一次 ; 默认300秒;

leader.imbalance.check.interval.seconds partition = 300

总结

这份面试题几乎包含了他在一年内遇到的所有面试题以及答案,甚至包括面试中的细节对话以及语录,可谓是细节到极致,甚至简历优化和怎么投简历更容易得到面试机会也包括在内!也包括教你怎么去获得一些大厂,比如阿里,腾讯的内推名额!

某位名人说过成功是靠99%的汗水和1%的机遇得到的,而你想获得那1%的机遇你首先就得付出99%的汗水!你只有朝着你的目标一步一步坚持不懈的走下去你才能有机会获得成功!

成功只会留给那些有准备的人!

[外链图片转存中…(img-PhjQrRsz-1715500797126)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值