kafka源码解析-分区副本的分配

主要介绍是未指定机架信息的分配策略,kafka版本是2.0.0,具体实现为kafka.admin.AdminUtils.scala文件中的

assignReplicasToBrokersRackUnaware()方法,该方法的内容如下:

private def assignReplicasToBrokersRackUnaware(nPartitions: Int,//分区数
                                                 replicationFactor: Int,//副本因子
                                                 brokerList: Seq[Int],//集群中broker列表
                                                 fixedStartIndex: Int,//起始索引,即第一个副本分配的位置,默认值为-1
                                                 startPartitionId: Int): Map[Int, Seq[Int]] = {//起始分区编号,默认值为-1
    // fixedStartIndex表示第一个副本分配的位置,默认为-1
    // startPartitionId表示起始分区编号,默认为-1
    // ret表示<partition,Seq[replica所在brokerId]>的关系
    val ret = mutable.Map[Int, Seq[Int]]()//保存分配结果的集合
    val brokerArray = brokerList.toArray //brokerId的列表
    // 如果起始索引fixedStartIndex小于0,则根据broker列表长度随机生成一个,以此来保证是有效的brokerId
    val startIndex = if (fixedStartIndex >= 0) fixedStartIndex else rand.nextInt(brokerArray.length)
    // 确保起始分区号不小于0
    var currentPartitionId = math.max(0, startPartitionId)
    // 指定了副本的间隔,目的是为了更均匀地将副本分配到不同的broker上
    var nextReplicaShift = if (fixedStartIndex >= 0) fixedStartIndex else rand.nextInt(brokerArray.length)
    // 轮询所有分区,将每个分区的副本分配到不同的broker上
    for (_ <- 0 until nPartitions) {
      if (currentPartitionId > 0 && (currentPartitionId % brokerArray.length == 0))
        nextReplicaShift += 1
      val firstReplicaIndex = (currentPartitionId + startIndex) % brokerArray.length
      val replicaBuffer = mutable.ArrayBuffer(brokerArray(firstReplicaIndex))
      // 保存该分区所有副本分配的broker集合
      for (j <- 0 until replicationFactor - 1)
        // 为其余的副本分配broker
        replicaBuffer += brokerArray(replicaIndex(firstReplicaIndex, nextReplicaShift, j, brokerArray.length))
      // 保存该分区所有副本的分配信息
      ret.put(currentPartitionId, replicaBuffer)
      // 继续为下一个分区分配副本
      currentPartitionId += 1
    }
    ret
  }

该方法参数列表中的 fixedStartlndex 和 startPartitionld 值是从上游的方法 中调用传下来的,
都是一 1 ,分别表示第一个副本分配的位置和起始分区编号。 assignReplicasToBrokersRackUnaware
()方法的核心是遍历 每 个分区 partition , 然后从 brokerArray ( brokerld 的列 表)中 选取
replicationFactor 个 brokerld 分配给这个 partition 。

该方法首先创建一个可变的 Map 用来存放该方法将要返回的结果 ,即分区 partition 和分配
副本的映射关系 。 由于 fixedStartlndex 为 1 ,所以 startlndex 是一个随机数,用来计算一个起始
分配的 brokerId,同时又因为 startPartitionld 为一l , 所 以 currentPartitionld 的值为 0,可见默认情
况下创建主题时总是从编号为 0 的分区依次轮询进行分配 。

nextReplicaShift 表示下一次副本分配相对于前一次分配的位移量 ,从字面上理解有 点绕 口 。
举个例子 : 假设集群中有 3 个 broker 节点 , 对应于代码 中的 brokerArray,创建的某个主题中有
3 个副本和 6 个分区,那么 首先从 partitionld ( partition 的编号) 为 0 的 分区开始进行分配,假
设第一次计算(由 rand .nextlnt(brokerArray.length)随机产生)得到的nextReplicaShift的值为1,第
一次随机产生的 startlndex 值为 2,那么 partitionld 为 0 的第一个副本的位置 ( 这里指的是
brokerArray 的数组下标 ) firstReplicalndex = (currentPartitionld + startlndex) % brokerArra川ength=
(0+2)%3=2 ,第二个副本的位置为 replicalndex(firstReplicalndex, nextReplicaShift, j , brokerArray.
length)= replicalndex(2, 1, 0, 3 )=?, 这里引 入了一个新 的方法 replicalndex() , 不
过这个方法很简单, 具体如下:
 

private def replicaIndex(firstReplicaIndex : Int , secondReplicaShift : Int ,
replicaIndex : Int , nBrokers : Int) : Int = {
    val shift = 1 + (secondReplicaShift + replicaIndex ) % ( nBrokers - 1 )
    (firstReplicaIndex + shift) % nBrokers
}

     说明:该方法是基于第一个副本分配的broker位置,再根据偏移量计算出后续副本被分配到的broker位置。

     在计算副本分配位置的时候,第一个副本的位置已经在循环外面计算过了,并且放入数组中了。后续计算剩余副本的

过程只计算了副本数-1次。在副本为3的情况下,replicaIndex的值只能是0和1。secondReplicaShift的值在分区不变的情况是不会变化的,一直是1。那么计算偏移量公式(secondReplicaShift + replicaIndex ) % ( nBrokers - 1 )可以理解为:分区副本循环一轮broker的偏移量由secondReplicaShift控制,同一分区的副本偏移量由replicaIndex控制。对broker数量-1(3-1)取余只能在0和1中,再加1的话,偏移量就只能是1和2,这样副本的偏移量不会等于0,也就分配的均匀了。

     继续计算replicaIndex(2, 1, 0, 3) = (2+(1+(1+0)%(3-1)))%3 = 1。继续计算下一个副本的位置replicaIndex(2, 1, 1, 3)=(2+(1+(1+1)%(3-1)))%3=0。所以partitionId为0的副本分配位置列表[2,1,0]。

给出最后的分配结果,buffer中的第一个为Leader副本。

分区0 ArrayBuffer(2, 1, 0)
分区1 ArrayBuffer(0, 2, 1)
分区2 ArrayBuffer(1, 0, 2)
分区3 ArrayBuffer(2, 0, 1)
分区4 ArrayBuffer(0, 1, 2)
分区5 ArrayBuffer(1, 2, 0)

参考:《深入理解kafka:核心设计与实践原理》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值