kafka源码分析之kafkacluster的管理-KafkaController

本文深入分析了KafkaController在Kafka集群中的作用,包括控制器的创建与启动、领导者的选举流程、监听分区变化及处理、以及定时平衡操作。在启动时,KafkaController负责选举出集群的领导者,并处理Broker的上线、下线、分区的管理和重新分配等任务。此外,文章详细介绍了选举过程,包括启动时的选举和Broker成为领导者后的处理步骤,确保集群的稳定运行。
摘要由CSDN通过智能技术生成

KafkaController

说明,这个实例主要用于对kafka cluster进行管理,一个kafkacluster表示同一个zk环境下所有的broker的集合,在这个cluster中需要有一个broker被选举成为leader,用于管理其它的broker的上线与下线的处理,对partition与副本的分配,管理topic的添加/修改/删除操作。

实例创建与启动

/* start kafka controller */
kafkaController 
new KafkaController(configzkUtilsbrokerState,

kafkaMetricsTimemetricsthreadNamePrefix)
kafkaController.startup()

 

实例初始化:

this.logIdent "[Controller " + config.brokerId "]: "
private var 
isRunning true
private val 
stateChangeLogger = KafkaController.stateChangeLogger
val controllerContext new ControllerContext(zkUtilsconfig.zkSessionTimeoutMs)

 

用于对partition的状态进行维护与更新操作.
val partitionStateMachine new PartitionStateMachine(this)

用于对每个partition的每一个副本的状态进行维护与更新操作.
val replicaStateMachine new ReplicaStateMachine(this)

 

生成用于监听leader发生变化的zookeeper的监听,用于当前的broker正在成为leader或者broker正在失去leader.
private val controllerElector new ZookeeperLeaderElector(controllerContext

      ZkUtils.ControllerPathonControllerFailover,
      onControllerResignationconfig.brokerId)

private val autoRebalanceScheduler new KafkaScheduler(1)
var deleteTopicManager: TopicDeletionManager = null

 

下面生成用于对partition的leader的选择的对应的选择器,包含partition加载时,重新分配时,首先副本,controll下线
val 
offlinePartitionSelector new 

     OfflinePartitionLeaderSelector(controllerContextconfig)
private val reassignedPartitionLeaderSelector new 

     ReassignedPartitionLeaderSelector(controllerContext)
private val preferredReplicaPartitionLeaderSelector new 

     PreferredReplicaPartitionLeaderSelector(controllerContext)
private val controlledShutdownPartitionLeaderSelector new 

     ControlledShutdownLeaderSelector(controllerContext)
private val brokerRequestBatch new ControllerBrokerRequestBatch(this)

这里生成几个用于监听partition的修改与isr的修改的监听器.
private val partitionReassignedListener new PartitionsReassignedListener(this)

 

这个用于在对partition的首先副本进行显示重新修改时,

用于监听对/admin/preferred_replica_election路径的修改.
private val preferredReplicaElectionListener new 

             PreferredReplicaElectionListener(this)

 

用于监听当partition的leader发生变化时,更新partitionLeadershipInfo集合的内容,同时向所有的brokers节点发送metadata修改的请求.
private val isrChangeNotificationListener new 

            IsrChangeNotificationListener(this)

 

KafkaController的实例启动:

def startup() = {
  inLock(controllerContext.controllerLock) {
    info("Controller starting up")

这里有于监听对ZK的session的注册,当向zk进行注册时,生成一个session后会调用onControllerResignation函数.这个函数会先取消对zookeeper的监听(partition相关),停止掉对应的管理组合.
    registerSessionExpirationListener()
    isRunning true

 

在/controller的zk路径上注册监听LeaderChangeListener实例.并执行选举.
    
controllerElector.startup
    info("Controller startup complete")
  }
}

 

leader的选举与动作

启动时的选举

通过在controller启动时调用ZookeeperLeaderElector实例中的startup函数.

这个函数在/controller中注册一个LeaderChangeListener的监听器,用于监听controller中的内容改变与删除.并执行函数进行elect选举.

def elect: Boolean = {
  val timestamp = SystemTime.milliseconds.toString
  val electString = Json.encode(Map("version" -> 1,

         "brokerid" -> brokerId"timestamp" -> timestamp))
 

这里直接从zookeeper中找到/controller路径下的内容,如果路径下存在内容时,直接读取这个节点的内容,并解析出leaderid(也就是被选为leader的broker),如果leader已经被选举出来,直接返回.
 leaderId = getControllerID 
  
  
if(leaderId != -1) {
     debug("Broker %d has been elected as leader, so stopping the election 

           process.".format(leaderId))
     return amILeader
  }

流程执行到这里,表示当前还没有broker节点被选举为leader.
  try {

这里尝试把当前节点注册成kafka的leader,通过create函数,如果这个函数执行成功,没有返回异常时,表示当前节点被选举成了leader,把当前的brokerId设置到leaderId的属性上.
    val zkCheckedEphemeral = new ZKCheckedEphemeral(electionPath,
                        electString,                                               

                        controllerContext.zkUtils.zkConnection.getZookeeper,

                        JaasUtils.isZkSecurityEnabled())
    zkCheckedEphemeral.create()
    info(brokerId + " successfully elected as leader")
    leaderId = brokerId

 

这里执行生成此实例传入的函数来执行leader的进入,这个函数的实现为KafkaController中onControllerFailover的函数.
    onBecomingLeader()
  } catch {
    case e: ZkNodeExistsException =>

如果通过ZKCheckedEphemeral去在zk中创建内容时失败,返回的错误是节点已经存在时,表示在这个kafka的server注册前,已经被其它的节点注册成了leader,读取/controller的路径,并设置其内容中的brokerId为leaderId.
      // If someone else has written the path, then
      leaderId 
= getControllerID 

      if (leaderId != -1)
        debug("Broker %d was elected as leader instead of broker %d"

            .format(leaderIdbrokerId))
      else
        
warn("A leader has been elected but just resigned, this will result in another 

             round of election")

    case e2: Throwable =>

如果是其它的错误,设置leaderId的值为-1,同时删除/controller的路径的内容.
      error("Error while electing or becoming leader on broker %d"

          .format(brokerId)e2)
      resign()
  }

这里返回的值如果是true表示当前节点就是leader,否则表示当前节点不是leader,对应的leaderId值就是leader节点的brokerId.


  amILeader
}

 

Broker被选举成leadercontroller的处理

当当前的kafka server被选举成为leader,会执行KafkaController中的onControllerFailover函数来处理leader的进入.

def onControllerFailover() {
  if(isRunning) {
    info("Broker %d starting become controller state 

           transition".format(config.brokerId))
    //read controller epoch from zk
   

/controller_epoch路径上读取controllerepoch的值,这个值用于控制controller的切换.

把读取到的值存储到controllerContext中的epochepochZkVersion属性中.

     readControllerEpochFromZookeeper()

 

更新controllerContext中的epoch的值(加1),并持久化到/controller_epoch路径上.
    // increment the controller epoch
    
incrementControllerEpoch(zkUtils.zkClient)

 

注册对/admin/reassign_partitions节点的监听处理程序,由PartitionsReassignedListener实现.用于监听partition的重新分配的动作.主要用于监听节点的内容修改
    
registerReassignedPartitionsListener()

 

注册对/isr_change_notification节点的监听处理程序,这个节点主要用于通知partitoin的isr的变化,由IsrChangeNotificationListener实现.主要用于监听节点的内容修改,
    registerIsrChangeNotificationListener()

 

注册对/admin/preferred_replica_election节点的监听处理程序,这个节点用于对副本的首选节点进行处理,由PreferredReplicaElectionListener实现.主要用于监听节点的内容修改.
    registerPreferredReplicaElectionListener()

 

在partitionStateMachine中对/brokers/topics节点注册监听处理程序,用于监听topic的修改,由TopicChangeListener实现.主要用于监听这个节点的子节点的修改.

如果配置有deletetopic的启用时,通过配置delete.topic.enable,默认为false.如果这个值配置为true时,对/admin/delete_topics节点注册一个DeleteTopicsListener监听处理程序,用于监听这个节点下的子节点的修改.
    partitionStateMachine.registerListeners()

 

/brokers/ids节点注册一个BrokerChangeListener监听处理程序,用于监听这个节点的子节点的修改,主要用于监听broker的的改变.
    replicaStateMachine.registerListeners()

 

初始化controller的上下文.
    initializeControllerContext()

 

启动对broker状态的监听与partition的状态监听的实例.
    replicaStateMachine.startup()
    partitionStateMachine.startup()

 

根据现在kafka中所有的topic,对/brokers/topics/topicname节点注册一个AddPartitionsListener监听处理程序,用于监听这个topic的修改.
    // register the partition change listeners for all existing topics on failover
    controllerContext
.allTopics.foreach(topic => 

        partitionStateMachine.registerPartitionChangeListener(topic))
    info("Broker %d is ready to serve as the new controller with epoch 

        %d".format(config.brokerIdepoch))

 

设置当前的broker的状态为RunningASController的broker,表示这是一个leader的节点.
    brokerState.newState(RunningAsController)

 

这里对未完成partition的副本重新分配的partitionsBeingReassigned集合进行迭代,执行如下的流程处理:

1,根据准备重新分配的partition的副本所在的节点集合,检查当前liveBrokers中是否都存在这些节点,如果要重新分配的节点集合中有在liveBrokers中不包含的节点,表示要分配的副本所在节点有没有启动的节点,throw exception,

2,根据需要重新分配的partition从partitionReplicaAssignment集合中找到对应的partition的信息,这个集合中存储了已经分配的partition的副本信息,如果在已经分配的partition的集合中找不到这个partition,throw exception.

3,如果准备重新分配的副本节点集合与现在partitionReplicaAssignment集合中parition对应的副本节点集合是相同的内容,表示重新分配是没有必要的,throw exception.

4,这种情况表示重新分配的副本节点集合对应的节点都已经启动,同时这个集合与现在此partition对应的分配副本节点集合不相同,执行如下的子流程:

4,1,在/brokers/topics/tpname/partitions/pid/state路径上生成注册一个用于监听isr的变化的ReassignedPartitionsIsrChangeListener监听程序.

4,2,在deleteTopicManager中检查这个topic是否是需要删除的topic,如果是,添加到准备删除的topic的集合中.

4,3,执行对partition中副本的重新分配,通过onPartitionReassignment函数.
    maybeTriggerPartitionReassignment()

 

这里对还未完成首选副本分配的partition进行首选副本分配的操作,这些未分配首选副本存储在partitionsUndergoingPreferredReplicaElection集合中.

1,首先检查对应的partitions的topic是否是已经被删除的topic,如果包含有要删除的topic时,把对应的partitions集合添加到待删除的topic partitions的集合中.

2,通过partitionStateMachine实例修改要进行首选副本分配的所有的partitions的状态为OnlinePartition.并通过preferredReplicaPartitionLeaderSelector实例进行partition的首选副本的选择操作,通过读取/brokers/topics/topicname/partitions/pid/state路径的isr的信息,如果这个路径还不存在时,根据当前partition的所有活着的副本集合,取第一个副本为leader,并把这个副本集合存储到这个路径中,根据读取这个路径的信息,通过leaderSelector来进行首选副本的分配.

3,更新partitionLeadershipInfo集合的内容,把这个partition对应的isr存储到这个集合中,并向对应的broker节点发送LeaderAndIsrRequest请求.

4,移出partitionsUndergoingPreferredReplicaElection集合中的内容,

        并删除/admin/preferred_replica_election节点的数据.
    maybeTriggerPreferredReplicaElection()

 

向所有的bro

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值