Spark Streaming自适应Kafka分区动态变化

  当业务数据增长比较明显,那么Kafka吞吐量,刚开始创建的topic数目和分区数目可能满足不了并发需求,需要增加分区。新增加的分区会有生产者往里面写数据,而Spark Streaming跟Kafka 0.8版本结合的API是满足不了动态发现Kafka新增topic或者分区的需求的。
  从Spark Streaming源码出发查看分区检测是在哪做的,对于批处理的Spark Streaming任务来说,分区检测应该在每次job生成获取kafkaRDD,来给kafkaRDD确定分区数并且每个分区赋值offset范围的时候有牵扯,而这段代码就在DirectKafkaInputDStream#compute方法中。compute方法源码:

/*
获取当前生成KafkaRDD每个分区消费的offset的最大值,
进入latestLeaderOffsets进一步去看,可以发现下面一行代码
*/
val untilOffsets = clamp(latestLeaderOffsets(maxRetries))
/*
根据currentOffsets信息来获取最大的offset,
由于它只是根据currentOffsets信息来获取最大的offset,
没有去感知新增的分区,所以Spark Streaming与kafka 0.8结合是不能动态感知分区的
*/
val o = kc.getLatestLeaderOffsets(currentOffsets.keySet)

对于Kafka 0.10版本,进入DirectKafkaInputDStream的compute,第一行代码也是val untilOffsets = clamp(latestOffsets()),在latestOffsets里面可以看到Kafka 0.10版本与SparkStreaming结合支持新增分区检测

 /**
   * Returns the latest (highest) available offsets, taking new partitions into account.
   */
  protected def latestOffsets(): Map[TopicPartition, Long] = {
    val c = consumer
    paranoidPoll(c)
    // 获取所有的分区信息
    val parts = c.assignment().asScala

    // make sure new partitions are reflected in currentOffsets
    // 做差获取新增的分区信息
    val newPartitions = parts.diff(currentOffsets.keySet)
    // position for new partitions determined by auto.offset.reset if no commit
    // 新分区消费位置,没有记录的化是由auto.offset.reset决定
    currentOffsets = currentOffsets ++ newPartitions.map(tp => tp -> c.position(tp)).toMap
    // don't want to consume messages, so pause
    c.pause(newPartitions.asJava)
    // find latest available offsets
    c.seekToEnd(currentOffsets.keySet.asJava)
    parts.map(tp => tp -> c.position(tp)).toMap
  }

获取所有的partition信息,然后得到新的partition并且将新的partition加入到currentOffsets这个变量里,这就实现了动态Kafka分区检测。

你可能感兴趣的:(Spark)