ControllerShutdownRequest分析

Kafka提供的Controlled Shutdown机制允许用户主动且优雅地关闭Broker实例,确保日志文件完全同步到磁盘并完成Leader副本迁移,减少分区不可用的时间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前面我们知道,当broker宕机我们可以根据注册BrokerChangeListener来监听zookeeper中"/brokers/ids"子节点变化,处理broker宕机的场景。

但是在有些场景中,用户希望主动关闭正常运行的broker,比如更换硬件,系统升级,修改kafka配置等。如果依然采用上述方式,那么就不太好了,这里Kafka提供了Controlled Shutdown的方式来关闭broker实例,其好处在于:

# 让日志文件可以完全同步在磁盘上,broker下次上线不需要进行Log的恢复操作

# 这种方式在关闭之前,会对其上的leader副本进行迁移,减少分区不可用时间

KafkaController.shutdownBroker是ControlledShutdownRequest核心,使用ControlledShutdownLeadSelector重新选举Leader副本和Isr列表,实现Leader副本的迁移。ControlledShutdownLeadSelector主要就是:从当前ISR集合列表移除正在关闭的副本,把剩余的副本作为新的ISR列表,然后从新的ISR列表选举leader,需要向可用的副本集发送LeaderAndIsrRequest。

def shutdownBroker(id: Int) : Set[TopicAndPartition] = {
  if (!isActive()) {
    throw new ControllerMovedException("Controllermoved to another broker. Aborting controlled shutdown")
  }

  controllerContext.brokerShutdownLock synchronized {
    info("Shutting down broker " + id)
    inLock(controllerContext.controllerLock) {
      if (!controllerContext.liveOrShuttingDownBrokerIds.contains(id))
        throw new BrokerNotAvailableException("Brokerid %d does not exist.".format(id))
      controllerContext.shuttingDownBrokerIds.add(id)
      debug("All shutting down brokers: " + controllerContext.shuttingDownBrokerIds.mkString(","))
      debug("Live brokers: " + controllerContext.liveBrokerIds.mkString(","))
    }
    // 获取待关闭的broker上的分区和副本信息
   
val allPartitionsAndReplicationFactorOnBroker: Set[(TopicAndPartition, Int)] =
      inLock(controllerContext.controllerLock) {
        controllerContext.partitionsOnBroker(id)
          .map(topicAndPartition => (topicAndPartition, controllerContext.partitionReplicaAssignment(topicAndPartition).size))
      }
    //  遍历这些分区副本信息
   
allPartitionsAndReplicationFactorOnBroker.foreach {
      case(topicAndPartition, replicationFactor) =>
        inLock(controllerContext.controllerLock) {
          // 遍历每一个分区的leader信息
         
controllerContext
.partitionLeadershipInfo.get(topicAndPartition).foreach { currLeaderIsrAndControllerEpoch =>
            if (replicationFactor > 1) {// 是否开启副本机制
              //
检测leader是否处于待关闭的broker
             
if (currLeaderIsrAndControllerEpoch.leaderAndIsr.leader == id) {
                // 将分区状态置为OnlinePartition,此步骤主要是使用controlledShutdownPartitionLeaderSelector进行重新
                //
选择leaderISR列表,并将结果写入zookeeper,之后发送LeaderAndIsrRequestUpdateMetadataRequest
               
partitionStateMachine
.handleStateChanges(Set(topicAndPartition), OnlinePartition,
                  controlledShutdownPartitionLeaderSelector)
              } else {
                // 发送StopReplicaRequest(不删除副本)
               
try {
                  brokerRequestBatch.newBatch()
                  brokerRequestBatch.addStopReplicaRequestForBrokers(Seq(id), topicAndPartition.topic,
                    topicAndPartition.partition, deletePartition = false)
                  brokerRequestBatch.sendRequestsToBrokers(epoch)
                } catch {
                  case e : IllegalStateException => {
                    // Resign if the controller is in an illegal state
                   
error("Forcing the controller to resign")
                    brokerRequestBatch.clear()
                    controllerElector.resign()

                    throw e
                 
}
                }
                // 将副本转换成Offline状态
               
replicaStateMachine
.handleStateChanges(Set(PartitionAndReplica(topicAndPartition.topic,
                  topicAndPartition.partition, id)), OfflineReplica)
              }
            }
          }
        }
    }
    def replicatedPartitionsBrokerLeads() = inLock(controllerContext.controllerLock) {
      trace("All leaders = " + controllerContext.partitionLeadershipInfo.mkString(","))
      controllerContext.partitionLeadershipInfo.filter {
        case (topicAndPartition, leaderIsrAndControllerEpoch) =>
          leaderIsrAndControllerEpoch.leaderAndIsr.leader == id && controllerContext.partitionReplicaAssignment(topicAndPartition).size > 1
     
}.keys
   
}
    replicatedPartitionsBrokerLeads().toSet
 
}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

莫言静好、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值