带有结果的spark流式传输使用comitAsync发送到另一个主题

时间:2018-02-08 22:20:21

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

我正在使用提供here in spark streaming documentation的策略来提交kafka本身。我的流程是这样的: 主题A - > Spark Stream [foreachRdd进程 - >发送到主题b]将偏移量提交给主题A

    JavaInputDStream<ConsumerRecord<String, Request>> kafkaStream = KafkaUtils.createDirectStream(
            streamingContext,
            LocationStrategies.PreferConsistent(),
            ConsumerStrategies.<String, Request>Subscribe(inputTopics, kafkaParams)
    );

    kafkaStream.foreachRDD(rdd -> {
                OffsetRange[] offsetRanges = ((HasOffsetRanges) rdd).offsetRanges();
                rdd.foreachPartition(
                        consumerRecords -> {
                            OffsetRange o = offsetRanges[TaskContext.get().partitionId()];
                            System.out.println(String.format("$s %d %d $d", o.topic(), o.partition(), o.fromOffset(), o.untilOffset()));
                            consumerRecords.forEachRemaining(record -> doProcess(record));
                        });

                ((CanCommitOffsets) kafkaStream.inputDStream()).commitAsync(offsetRanges);
            }
    );

因此,让我们说RDD从主题A获取10个事件,并且在处理每个事件时,我向主题B发送新事件。现在假设其中一个响应失败。现在我不想将该特定偏移量提交给主题A.主题A和B具有相同数量的分区N.因此每个RDD应该从同一分区消耗。什么是继续处理的最佳策略?如何重置流以尝试从主题A处理这些事件,直到成功为止?我知道如果我不能继续处理该分区而不提交,因为这会自动移动偏移量并且不会再次处理失败的记录。

我不知道如何使stream / rdd继续尝试仅为该分区处理相同的消息,而其他分区/ rdd可以继续工作。如果我从特定的RDD中抛出异常,我的工作会发生什么。它会失败吗?我需要手动重启吗?对于普通消费者,您可以重试/恢复,但我不确定Streaming会发生什么。

1 个答案:

答案 0 :(得分:0)

这是我提出的,它接受输入数据,然后使用输出主题发送请求。必须在foreach循环内创建生成器,否则spark将尝试序列化并将其发送给所有worker。请注意,响应是异步发送的。这意味着我在这个系统中使用至少一个语义。

kafkaStream.foreachRDD(rdd -> {
        OffsetRange[] offsetRanges = ((HasOffsetRanges) rdd.rdd()).offsetRanges();
        rdd.foreachPartition(
                partition -> {
                    OffsetRange o = offsetRanges[TaskContext.get().partitionId()];
                    System.out.println(String.format("%s %d %d %d", o.topic(), o.partition(), o.fromOffset(), o.untilOffset()));

                    // Print statements in this section are shown in the executor's stdout logs
                    KafkaProducer<String, MLMIOutput> producer = new KafkaProducer(producerConfig(o.partition()));
                    partition.forEachRemaining(record -> {

                        System.out.println("request: "+record.value());

                        Response data = new  Response …
                        // As as debugging technique, users can write to DBFS to verify that records are being written out
                        // dbutils.fs.put("/tmp/test_kafka_output",data,true)
                        ProducerRecord<String, Response> message = new ProducerRecord(outputTopic, null, data);
                        Future<RecordMetadata> result = producer.send(message);
                        try {
                            RecordMetadata metadata = result.get();
                            System.out.println(String.format("offset='$d' partition='%d' topic='%s'timestamp='$d",
                            metadata.offset(),metadata.partition(),metadata.topic(),metadata.timestamp()));
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } catch (ExecutionException e) {
                            e.printStackTrace();
                        }
                    });
                    producer.close();
                });

        ((CanCommitOffsets) kafkaStream.inputDStream()).commitAsync(offsetRanges);
    }

);