【kafka源码】kafka分区副本的分配规则

toCreate: Map[String, CreatableTopic],

includeConfigsAndMetatadata: Map[String, CreatableTopicResult],

responseCallback: Map[String, ApiError] => Unit): Unit = {

// 1. map over topics creating assignment and calling zookeeper

val brokers = metadataCache.getAliveBrokers.map { b => kafka.admin.BrokerMetadata(b.id, b.rack) }

val metadata = toCreate.values.map(topic =>

try {

val assignments = if (topic.assignments().isEmpty) {

AdminUtils.assignReplicasToBrokers(

brokers, resolvedNumPartitions, resolvedReplicationFactor)

} else {

val assignments = new mutable.HashMap[Int, Seq[Int]]

// Note: we don’t check that replicaAssignment contains unknown brokers - unlike in add-partitions case,

// this follows the existing logic in TopicCommand

topic.assignments.asScala.foreach {

case assignment => assignments(assignment.partitionIndex()) =

assignment.brokerIds().asScala.map(a => a: Int)

}

assignments

}

trace(s"Assignments for topic $topic are $assignments ")

}

  1. 以上有两种方式,一种是我们没有指定分区分配的情况也就是没有使用参数--replica-assignment;一种是自己指定了分区分配

1. 自己指定了分区分配规则

从源码中得知, 会把我们指定的规则进行了包装,注意它并没有去检查你指定的Broker是否存在;

2. 自动分配 AdminUtils.assignReplicasToBrokers

在这里插入图片描述

  1. 参数检查: 分区数>0; 副本数>0; 副本数<=Broker数 (如果自己未定义会直接使用Broker中个配置)

  2. 根据是否有 机架信息来进行不同方式的分配;

  3. 要么整个集群都有机架信息,要么整个集群都没有机架信息; 否则抛出异常

副本分配的几个原则:

  1. 将副本平均分布在所有的 Broker 上;

  2. partition 的多个副本应该分配在不同的 Broker 上;

  3. 如果所有的 Broker 有机架信息的话, partition 的副本应该分配到不同的机架上。

无机架方式分配

AdminUtils.assignReplicasToBrokersRackUnaware

/**

  • 副本分配时,有三个原则:

    1. 将副本平均分布在所有的 Broker 上;
    1. partition 的多个副本应该分配在不同的 Broker 上;
    1. 如果所有的 Broker 有机架信息的话, partition 的副本应该分配到不同的机架上。
  • 为实现上面的目标,在没有机架感知的情况下,应该按照下面两个原则分配 replica:

    1. 从 broker.list 随机选择一个 Broker,使用 round-robin 算法分配每个 partition 的第一个副本;
    1. 对于这个 partition 的其他副本,逐渐增加 Broker.id 来选择 replica 的分配。

*/

private def assignReplicasToBrokersRackUnaware(nPartitions: Int,

replicationFactor: Int,

brokerList: Seq[Int],

fixedStartIndex: Int,

startPartitionId: Int): Map[Int, Seq[Int]] = {

val ret = mutable.MapInt, Seq[Int]

// 这里是上一层传递过了的所有 存活的Broker列表的ID

val brokerArray = brokerList.toArray

//默认随机选一个index开始

val startIndex = if (fixedStartIndex >= 0) fixedStartIndex else rand.nextInt(brokerArray.length)

//默认从0这个分区号开始

var currentPartitionId = math.max(0, startPartitionId)

var nextReplicaShift = if (fixedStartIndex >= 0) fixedStartIndex else rand.nextInt(brokerArray.length)

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))

for (j <- 0 until replicationFactor - 1)

replicaBuffer += brokerArray(replicaIndex(firstReplicaIndex, nextReplicaShift, j, brokerArray.length))

ret.put(currentPartitionId, replicaBuffer)

currentPartitionId += 1

}

ret

}

//主要的计算间隔数的方法

private def replicaIndex(firstReplicaIndex: Int, secondReplicaShift: Int, replicaIndex: Int, nBrokers: Int): Int = {

val shift = 1 + (secondReplicaShift + replicaIndex) % (nBrokers - 1)

(firstReplicaIndex + shift) % nBrokers

}

  1. 从 broker.list 随机选择一个 Broker,使用 round-robin 算法分配每个 partition 的第一个副本;

  2. 对于这个 partition 的其他副本,逐渐增加 Broker.id 来选择 replica 的分配。

  3. 对于副本分配来说,每经历一次Broker的遍历,则第一个副本跟后面的副本直接的间隔+1;

从代码和描述来看,可能理解不是很简单,但是下面的图我相信会让你非常快速的理解;

我们稍微在这段代码里面节点日志

在这里插入图片描述

然后写段单元测试,执行一下,看看分配过程

Broker列表{0,1,2,3,4} 分区数 10 副本数3 起始随机BrokerId=0; 起始随机nextReplicaShift=0

@Test

def testReplicaAssignment2(): Unit = {

val brokerMetadatas = (0 to 4).map(new BrokerMetadata(_, None))

AdminUtils.assignReplicasToBrokers(brokerMetadatas, 10, 3, 0)

}

输出:

起始随机startIndex:0;起始随机nextReplicaShift:0

(p-0,ArrayBuffer(0, 1, 2))

(p-1,ArrayBuffer(1, 2, 3))

(p-2,ArrayBuffer(2, 3, 4))

(p-3,ArrayBuffer(3, 4, 0))

(p-4,ArrayBuffer(4, 0, 1))

变更nextReplicaShift:1

(p-5,ArrayBuffer(0, 2, 3))

(p-6,ArrayBuffer(1, 3, 4))

(p-7,ArrayBuffer(2, 4, 0))

(p-8,ArrayBuffer(3, 0, 1))

(p-9,ArrayBuffer(4, 1, 2))

看图

在这里插入图片描述

上面是分配的情况,我们每一行每一行看, 每次都是先把每个分区的副本分配好的;

  1. 最开始的时候,随机一个Broker作为第一个来接受P0; 这里我们假设随机到了 broker-0; 所以第一个P0在broker-0上; 那么第二个p0-2的位置跟nextReplicaShit有关,这个值也是随机的,这里假设随机的起始值也是0; 这个值意思可以简单的理解为,第一个副本和第二个副本的间隔;

  2. 因为nextReplicaShit=0; 所以p0的分配分别再 {0,1,2}

  3. 然后再分配后面的分区,分区的第一个副本位置都是按照broker顺序遍历的;

  4. 直到这一次的broker遍历完了,那么就要重头再进行遍历了, 同时nextReplicaShit=nextReplicaShit+1=1;

  5. P5-1 再broker-0上,然后p5-2要跟p5-1间隔nextReplicaShit=1个位置,所以p5-2这时候在broker-2上,P5-3则在P5-2基础上顺推一位就行了,如果顺推的位置上已经有了副本,则继续顺推到没有当前分区副本的Broker

  6. 如果分区过多,有可能nextReplicaShift就变的挺大,在算第一个跟第二个副本的间隔的时候,不用把第一个副本算进去;

假如下面起始是 5,其中经历过的间隔就是 ( 1->2->3->4->1 )所以PN-2就落在 BrokerLIst[2]上了

在这里插入图片描述

Broker列表{0,1,2,3,4} 分区数 11 副本数3 起始随机BrokerId=0; 起始随机nextReplicaShift=0

在上面基础上,再增加1个分区,你知道会怎么分配么

结果:

起始随机startIndex:0;起始随机nextReplicaShift:0

(p-0,ArrayBuffer(0, 1, 2))

(p-1,ArrayBuffer(1, 2, 3))

(p-2,ArrayBuffer(2, 3, 4))

(p-3,ArrayBuffer(3, 4, 0))

(p-4,ArrayBuffer(4, 0, 1))

变更nextReplicaShift:1

(p-5,ArrayBuffer(0, 2, 3))

(p-6,ArrayBuffer(1, 3, 4))

(p-7,ArrayBuffer(2, 4, 0))

(p-8,ArrayBuffer(3, 0, 1))

(p-9,ArrayBuffer(4, 1, 2))

变更nextReplicaShift:2

(p-10,ArrayBuffer(0, 3, 4))

(p-11,ArrayBuffer(1, 4, 0))

在这里插入图片描述

Broker列表{0,1,2,3,4} 分区数 10 副本数4 起始随机BrokerId=0; 起始随机nextReplicaShift=0

起始随机startIndex:0;起始随机nextReplicaShift:0

(p-0,ArrayBuffer(0, 1, 2, 3))

(p-1,ArrayBuffer(1, 2, 3, 4))

(p-2,ArrayBuffer(2, 3, 4, 0))

(p-3,ArrayBuffer(3, 4, 0, 1))

(p-4,ArrayBuffer(4, 0, 1, 2))

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
img

总结

谈到面试,其实说白了就是刷题刷题刷题,天天作死的刷。。。。。

为了准备这个“金三银四”的春招,狂刷一个月的题,狂补超多的漏洞知识,像这次美团面试问的算法、数据库、Redis、设计模式等这些题目都是我刷到过的

并且我也将自己刷的题全部整理成了PDF或者Word文档(含详细答案解析)

我的美团offer凉凉了?开发工程师(Java岗)三面结束等通知...

66个Java面试知识点

架构专题(MySQL,Java,Redis,线程,并发,设计模式,Nginx,Linux,框架,微服务等)+大厂面试题详解(百度,阿里,腾讯,华为,迅雷,网易,中兴,北京中软等)

我的美团offer凉凉了?开发工程师(Java岗)三面结束等通知...

算法刷题(PDF)

我的美团offer凉凉了?开发工程师(Java岗)三面结束等通知...

到面试,其实说白了就是刷题刷题刷题,天天作死的刷。。。。。

为了准备这个“金三银四”的春招,狂刷一个月的题,狂补超多的漏洞知识,像这次美团面试问的算法、数据库、Redis、设计模式等这些题目都是我刷到过的

并且我也将自己刷的题全部整理成了PDF或者Word文档(含详细答案解析)

[外链图片转存中…(img-3TW9xCde-1710744677363)]

66个Java面试知识点

架构专题(MySQL,Java,Redis,线程,并发,设计模式,Nginx,Linux,框架,微服务等)+大厂面试题详解(百度,阿里,腾讯,华为,迅雷,网易,中兴,北京中软等)

[外链图片转存中…(img-Nal41AQK-1710744677363)]

算法刷题(PDF)

[外链图片转存中…(img-fCcDNETO-1710744677364)]

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值