(3.1)broker源码——topic创建原理

1:创建方式

有两种创建方式:
1:通过shell命令 kafka-topics.sh 创建一个 topic,可以设置相应的副本数让 Server 端自动进行 replica 分配,也可以直接指定手动 replica 的分配。
2:Server 端如果 auto.create.topics.enable 设置为 true 时,那么当 Producer 向一个不存在的 topic 发送数据时,该 topic 同样会被创建出来,此时,副本数默认是1。

1.1:创建 topic流程分析

在 Kafka 的安装目录下,通过下面这条命令可以创建一个 partition 为3,replica 为2的 topic(test)

./bin/kafka-topics.sh --create --topic test --zookeeper XXXX --partitions 3 --replication-factor 2

无论是生产者创建还是shell kafka-topics.sh 创建实际上是调用 kafka.admin.TopicCommand 的方法来创建 topic,其实现如下:

  def createTopic(zkClient: KafkaZkClient, opts: TopicCommandOptions) {
    val topic = opts.options.valueOf(opts.topicOpt)
    val configs = parseTopicConfigsToBeAdded(opts)
    val ifNotExists = opts.options.has(opts.ifNotExistsOpt)
    if (Topic.hasCollisionChars(topic))
      println("WARNING: Due to limitations in metric names, topics with a period ('.') or underscore ('_') could collide. To avoid issues it is best to use either, but not both.")
    val adminZkClient = new AdminZkClient(zkClient)
    try {
      if (opts.options.has(opts.replicaAssignmentOpt)) { //判断是否指定了副本分配
        val assignment = parseReplicaAssignment(opts.options.valueOf(opts.replicaAssignmentOpt))
        adminZkClient.createOrUpdateTopicPartitionAssignmentPathInZK(topic, assignment, configs, update = false)
      } else { //没有指定副本时,使用默认的算法
        CommandLineUtils.checkRequiredArgs(opts.parser, opts.options, opts.partitionsOpt, opts.replicationFactorOpt)
        val partitions = opts.options.valueOf(opts.partitionsOpt).intValue
        val replicas = opts.options.valueOf(opts.replicationFactorOpt).intValue
        val rackAwareMode = if (opts.options.has(opts.disableRackAware)) RackAwareMode.Disabled
                            else RackAwareMode.Enforced
       //执行创建topic逻辑,副本分配方式在此实现,详情见下方副本分配方式
        adminZkClient.createTopic(topic, partitions, replicas, configs, rackAwareMode) 
      }
      println("Created topic \"%s\".".format(topic))
    } catch  {
      case e: TopicExistsException => if (!ifNotExists) throw e
    }
  }

副本的分配方式
为了在不考虑机架的情况下

  • 1:从broker节点列表中的随机选择出首个副本分配位置,首个选出后通过循环分配每个分区的第一个副本。

  • 2:以递增的移位量分配每个分区的其余副本。举例和源码见下

具体源码实现在下面,可以发现主要步骤如下

  private def assignReplicasToBrokersRackUnaware(nPartitions: Int,//分区数
                          replicationFactor: Int,//副本因子,即几个副本
                          brokerList: Seq[Int],//集群中broker列表
                          fixedStartIndex: Int,//起始索引,第一个索引的分配位置,默认不设置为-1,起到循环中递增作用
                          					   //ret结果的replicaBuffer第一列的2-1-0的实现即是靠fixedStartIndex
                          startPartitionId: Int): Map[Int, Seq[Int]] = {

    // ret表示<partition,Seq[replica所在brokerId]>的关系
    val ret = mutable.Map[Int, Seq[Int]]()
    val brokerArray = brokerList.toArray
    //通过随机方式拿到首个副本分配节点
    val startIndex = if (fixedStartIndex >= 0) fixedStartIndex else rand.nextInt(brokerArray.length)
    var currentPartitionId = math.max(0, startPartitionId) //startPartitionId指定从哪个分区开始分配,默认-1

    //随机获取副本分配的间隔,随机数为1到brokerArray.length
    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

      }
      //算出首个副本分配的broker,第一个分区分配后,则第二次的分区分配为第一次分区位置+1
      val firstReplicaIndex = (currentPartitionId + startIndex) % brokerArray.length
      //replicaBuffer存储分配的副本集合
      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
  }

最终ret为分配结果,结果大致如下,buffer中的第一个为Leader副本。

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

分配结果拿到后进行topic和分区有效性验证,然后在zookeeper上注册topic信息,完成创建

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值