来自Kafka主题的Spark Streaming抛出偏移范围,没有重启流的选项

时间:2017-10-06 14:35:46

标签: scala apache-spark apache-kafka spark-streaming

我在Spark 2.1.1上运行了一个流媒体作业,轮询Kafka 0.10。我正在使用Spark KafkaUtils类来创建一个DStream,一切正常,直到我的数据由于保留策略而超出主题。我的问题出现在我停止工作以进行一些更改,如果任何数据已超出主题我收到错误说我的偏移量超出范围。我已经做了很多研究,包括查看火花源代码,我看到很多评论,比如这个问题中的评论:SPARK-19680 - 基本上说数据不应该无声地丢失 - 所以auto.offset.reset被火花忽略了。不过,我最大的问题是我现在可以做些什么?我的主题不会在spark中轮询 - 它在启动时因偏移异常而死亡。我不知道如何重置偏移,所以我的工作将重新开始。我没有启用检查点,因为我读到这些用途不可靠。我曾经有很多代码来管理偏移量,但是如果有任何提交的话,它似乎会忽略请求的偏移量,所以我目前正在管理这样的偏移量:

val stream = KafkaUtils.createDirectStream[String, T](
    ssc,
    PreferConsistent,
    Subscribe[String, T](topics, kafkaParams))

stream.foreachRDD { (rdd, batchTime) =>
    val offsets = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
    Log.debug("processing new batch...")

    val values = rdd.map(x => x.value())
    val incomingFrame: Dataset[T] = SparkUtils.sparkSession.createDataset(values)(consumer.encoder()).persist

    consumer.processDataset(incomingFrame, batchTime)
    stream.asInstanceOf[CanCommitOffsets].commitAsync(offsets)
}
ssc.start()
ssc.awaitTermination()

作为一种解决方法,我一直在改变我的群组ID,但这真的很蹩脚。我知道这是预期的行为,不应该发生,我只需要知道如何让流再次运行。任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:2)

这是我写的一段代码,直到将一个真正的解决方案引入spark-streaming-kafka。它基本上根据您设置的OffsetResetStrategy重置已经老化的分区的偏移量。只需给它提供相同的Map params, _params ,你提供给KafkaUtils。在从驱动程序调用KafkaUtils.create **** Stream()之前调用它。

final OffsetResetStrategy offsetResetStrategy = OffsetResetStrategy.valueOf(_params.get(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG).toString().toUpperCase(Locale.ROOT));
if(OffsetResetStrategy.EARLIEST.equals(offsetResetStrategy) || OffsetResetStrategy.LATEST.equals(offsetResetStrategy)) {
    LOG.info("Going to reset consumer offsets");
    final KafkaConsumer<K,V> consumer = new KafkaConsumer<>(_params);

    LOG.debug("Fetching current state");
    final List<TopicPartition> parts = new LinkedList<>();
    final Map<TopicPartition, OffsetAndMetadata> currentCommited = new HashMap<>();
    for(String topic: this.topics()) {
        List<PartitionInfo> info = consumer.partitionsFor(topic);
        for(PartitionInfo i: info) {
            final TopicPartition p = new TopicPartition(topic, i.partition());
            final OffsetAndMetadata m = consumer.committed(p);
            parts.add(p);
            currentCommited.put(p, m);
        }
    }
    final Map<TopicPartition, Long> begining = consumer.beginningOffsets(parts);
    final Map<TopicPartition, Long> ending = consumer.endOffsets(parts);

    LOG.debug("Finding what offsets need to be adjusted");
    final Map<TopicPartition, OffsetAndMetadata> newCommit = new HashMap<>();
    for(TopicPartition part: parts) {
        final OffsetAndMetadata m = currentCommited.get(part);
        final Long begin = begining.get(part);
        final Long end = ending.get(part);

        if(m == null || m.offset() < begin) {
            LOG.info("Adjusting partition {}-{}; OffsetAndMeta={} Begining={} End={}", part.topic(), part.partition(), m, begin, end);

            final OffsetAndMetadata newMeta;
            if(OffsetResetStrategy.EARLIEST.equals(offsetResetStrategy)) {
                newMeta = new OffsetAndMetadata(begin);
            } else if(OffsetResetStrategy.LATEST.equals(offsetResetStrategy)) {
                newMeta = new OffsetAndMetadata(end);
            } else {
                newMeta = null;
            }

            LOG.info("New offset to be {}", newMeta);
            if(newMeta != null) {
                newCommit.put(part, newMeta);
            }
        }

    }
    consumer.commitSync(newCommit);
    consumer.close();
}

答案 1 :(得分:1)

auto.offset.reset=latest/earliest仅在消费者首次启动时应用。

有Spark JIRA来解决此问题,直到那时我们需要现场解决。 https://issues.apache.org/jira/browse/SPARK-19680

答案 2 :(得分:0)

尝试

auto.offset.reset=latest

auto.offset.reset=earliest

最早:自动将偏移重置为最早的偏移量

最新:自动将偏移重置为最新偏移量

none:如果没有找到消费者组的先前偏移

,则向消费者抛出异常

其他任何事情:向消费者抛出异常。

影响什么偏移值将对应于最小和最大配置的另一件事是日志保留策略。想象一下,您的主题保留配置为1小时。您生成10条消息,然后一小时后再发布10条消息。最大的偏移量仍将保持不变,但最小的偏移量不能为0,因为Kafka已经删除了这些消息,因此最小的可用偏移量将为10。