Kafka Controller模块(一):集群元数据ControllerContext

ControllerContext储存kafka集群的元数据,定义如下:

class ControllerContext {
  val stats = new ControllerStats // Controller统计信息类
  var offlinePartitionCount = 0 // 离线分区计数器
  var shuttingDownBrokerIds: mutable.Set[Int] = mutable.Set.empty // 关闭中Broker的Id列表
  private var liveBrokers: Set[Broker] = Set.empty // 当前运行中Broker对象列表
  private var liveBrokerEpochs: Map[Int, Long] = Map.empty // 运行中Broker Epoch列表
  var epoch: Int = KafkaController.InitialControllerEpoch // Controller当前Epoch值
  var epochZkVersion: Int = KafkaController.InitialControllerEpochZkVersion // Controller对应ZooKeeper节点的Epoch值
  // Kafka 使用 epochZkVersion 来判断和防止 Zombie Controller。
  // 这也就是说,原先在老 Controller 任期内的 Controller 操作在新 Controller 不能成功执行,因为新 Controller 的 epochZkVersion 要比老 Controller 的大。

  var allTopics: Set[String] = Set.empty
  val partitionAssignments = mutable.Map.empty[String, mutable.Map[Int, ReplicaAssignment]] // 主题分区的副本列表
  val partitionLeadershipInfo = mutable.Map.empty[TopicPartition, LeaderIsrAndControllerEpoch] // 主题分区的Leader/ISR副本信息
  val partitionsBeingReassigned = mutable.Set.empty[TopicPartition] // 正处于副本重分配过程的主题分区列表
  val partitionStates = mutable.Map.empty[TopicPartition, PartitionState] // 主题分区状态列表
  val replicaStates = mutable.Map.empty[PartitionAndReplica, ReplicaState] // 主题分区的副本状态列表
  val replicasOnOfflineDirs: mutable.Map[Int, Set[TopicPartition]] = mutable.Map.empty // 不可用磁盘路径上的副本列表

  val topicsToBeDeleted = mutable.Set.empty[String] // 待删除主题列表

  val topicsWithDeletionStarted = mutable.Set.empty[String] // 已开启删除的主题列表
  val topicsIneligibleForDeletion = mutable.Set.empty[String] // 暂时无法执行删除的主题列表
}

ControllerStats

第一个是 ControllerStats 类的变量。它的完整定义如下:

// ControllerStats Controller 的一些统计信息.
// 目前,源码中定义了两大类统计指标:UncleanLeaderElectionsPerSec 和所有 Controller 事件状态的执行速率与时间。
private[controller] class ControllerStats extends KafkaMetricsGroup {
  // 统计每秒发生的Unclean Leader选举次数
  // 通常情况下,执行 Unclean Leader 选举可能造成数据丢失,一般不建议开启它。
  val uncleanLeaderElectionRate = newMeter("UncleanLeaderElectionsPerSec", "elections", TimeUnit.SECONDS)

  val rateAndTimeMetrics: Map[ControllerState, KafkaTimer] = ControllerState.values.flatMap { state =>
    // Controller事件通用的统计速率指标的方法
    // 由于 Controller 事件有很多种,对应的速率监控指标也有很多,有一些 Controller 事件是需要你额外关注的。
    // 举个例子,IsrChangeNotification 事件是标志 ISR 列表变更的事件,如果这个事件经常出现,说明副本的 ISR 列表经常发生变化,而这通常被认为是非正常情况。
    state.rateAndTimeMetricName.map { metricName =>
      state -> new KafkaTimer(newTimer(metricName, TimeUnit.MILLISECONDS, TimeUnit.SECONDS))
    }
  }.toMap
}

offlinePartitionCount

       该字段统计集群中所有离线或处于不可用状态的主题分区数量。ControllerContext 中的 updatePartitionStateMetrics

       方法根据给定主题分区的当前状态和目标状态,来判断该分区是否是离线状态的分区。如果是,则累加 offlinePartitionCount 字段的值,否则递减该值。

  private def updatePartitionStateMetrics(partition: TopicPartition,
                                          currentState: PartitionState,
                                          targetState: PartitionState): Unit = {
    // 如果该主题当前并未处于删除中状态
    if (!isTopicDeletionInProgress(partition.topic)) {
      // targetState表示该分区要变更到的状态
      // 如果当前状态不是OfflinePartition,即离线状态并且目标状态是离线状态
      // 这个if语句判断是否要将该主题分区状态转换到离线状态
      if (currentState != OfflinePartition && targetState == OfflinePartition) {
        offlinePartitionCount = offlinePartitionCount + 1
        // 如果当前状态已经是离线状态,但targetState不是
        // 这个else if语句判断是否要将该主题分区状态转换到非离线状态
      } else if (currentState == OfflinePartition && targetState != OfflinePartition) {
        offlinePartitionCount = offlinePartitionCount - 1
      }
    }
  }

  shuttingDownBrokerIds

        该字段保存所有正在关闭中的 Broker ID 列表,依靠这个字段来甄别 Broker 当前是否已关闭,因为处于关闭状态的 Broker 是不适合执行某些操作的。

  private def onBrokerFailure(deadBrokers: Seq[Int]): Unit = {
    info(s"Broker failure callback for ${deadBrokers.mkString(",")}")
    // deadBrokers:给定的一组已终止运行的Broker Id列表
    // 更新Controller元数据信息,将给定Broker从元数据的replicasOnOfflineDirs中移除
    deadBrokers.foreach(controllerContext.replicasOnOfflineDirs.remove)
    // 找出这些Broker上的所有副本对象
    val deadBrokersThatWereShuttingDown =
      deadBrokers.filter(id => controllerContext.shuttingDownBrokerIds.remove(id))
    if (deadBrokersThatWereShuttingDown.nonEmpty)
      info(s"Removed ${deadBrokersThatWereShuttingDown.mkString(",")} from list of shutting down brokers.")
    // 执行副本清扫工作
    val allReplicasOnDeadBrokers = controllerContext.replicasOnBrokers(deadBrokers.toSet)
    onReplicasBecomeOffline(allReplicasOnDeadBrokers)
    // 取消这些Broker上注册的ZooKeeper监听器
    unregisterBrokerModificationsHandler(deadBrokers)
  }

liveBrokers

        该字段保存当前所有运行中的 Broker 对象。ControllerContext 中定义了很多方法来管理该字段,如 addLiveBrokersAndEpochs、removeLiveBrokers 和 updateBrokerMetadata 等。

  // 每个 Broker 对象就是一个 <Id,EndPoint,机架信息 > 的三元组。
  def addLiveBrokersAndEpochs(brokerAndEpochs: Map[Broker, Long]): Unit = {
    liveBrokers = liveBrokers ++ brokerAndEpochs.keySet
    liveBrokerEpochs = liveBrokerEpochs ++
      (brokerAndEpochs map { case (broker, brokerEpoch) => (broker.id, brokerEpoch)})
  }

  def removeLiveBrokers(brokerIds: Set[Int]): Unit = {
    liveBrokers = liveBrokers.filter(broker => !brokerIds.contains(broker.id))
    liveBrokerEpochs = liveBrokerEpochs.filter { case (id, _) => !brokerIds.contains(id) }
  }
  
  def updateBrokerMetadata(oldMetadata: Broker, newMetadata: Broker): Unit = {
    liveBrokers -= oldMetadata
    liveBrokers += newMetadata
  }

liveBrokerEpochs

        该字段保存所有运行中 Broker 的 Epoch 信息。Kafka 使用 Epoch 数据防止 Zombie Broker,即一个非常老的 Broker 被选举成为 Controller。
def liveBrokerIds: Set[Int] = liveBrokerEpochs.keySet -- shuttingDownBrokerIds

partitionAssignments

       该字段保存所有主题分区的副本分配情况。
       比如,如果 Kafka 要获取某个 Broker 上的所有分区,那么,它可以这样定义

partitionAssignments.flatMap {
      case (topic, topicReplicaAssignment) => topicReplicaAssignment.filter {
        case (_, partitionAssignment) => partitionAssignment.replicas.contains(brokerId)
      }.map {
        case (partition, _) => new TopicPartition(topic, partition)
      }
    }.toSet

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值