Spark Streaming:微批并行执行

时间:2017-07-13 15:13:36

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

我们正在接收来自Kafka的火花流数据。在Spark Streaming中启动执行后,它只执行一个批处理,其余批处理开始在Kafka中排队。

  

我们的数据是独立的,可以是并行处理。

我们尝试了多个配置,包括多个执行器,核心,背压和其他配置,但到目前为止还没有任何工作。排队的消息很多,一次只处理了一个微批处理,其余的都保留在队列中。

我们希望最大程度地实现并行性,因此我们没有足够的可用资源,因此不会对任何微批处理进行排队。那么我们如何通过最大限度地利用资源来减少时间。

enter image description here

// Start reading messages from Kafka and get DStream
final JavaInputDStream<ConsumerRecord<String, byte[]>> consumerStream = KafkaUtils.createDirectStream(
        getJavaStreamingContext(), LocationStrategies.PreferConsistent(),
        ConsumerStrategies.<String, byte[]>Subscribe("TOPIC_NAME",
                sparkServiceConf.getKafkaConsumeParams()));

ThreadContext.put(Constants.CommonLiterals.LOGGER_UID_VAR, CommonUtils.loggerUniqueId());

JavaDStream<byte[]> messagesStream = consumerStream.map(new Function<ConsumerRecord<String, byte[]>, byte[]>() {
    private static final long serialVersionUID = 1L;
    @Override
    public byte[] call(ConsumerRecord<String, byte[]> kafkaRecord) throws Exception {
        return kafkaRecord.value();
    }
});

    // Decode each binary message and generate JSON array
        JavaDStream<String> decodedStream = messagesStream.map(new Function<byte[], String>() {
            private static final long serialVersionUID = 1L;

            @Override
            public String call(byte[] asn1Data) throws Exception {
                if(asn1Data.length > 0) {
                    try (InputStream inputStream = new ByteArrayInputStream(asn1Data);
                            Writer writer = new StringWriter(); ) {


                        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(asn1Data);
                        GZIPInputStream gzipInputStream = new GZIPInputStream(byteArrayInputStream);

                        byte[] buffer = new byte[1024];
                        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

                        int len;
                        while((len = gzipInputStream.read(buffer)) != -1) {
                            byteArrayOutputStream.write(buffer, 0, len);
                        }


                        return new String(byteArrayOutputStream.toByteArray());


                    } catch (Exception e) {
//                      
                        producer.flush();

                        throw e;
                    }
                } 

                return null;
            }
        });




// publish generated json gzip to kafka 
        cache.foreachRDD(new VoidFunction<JavaRDD<String>>() {
            private static final long serialVersionUID = 1L;

            @Override
            public void call(JavaRDD<String> jsonRdd4DF) throws Exception {
                //Dataset<Row> json = sparkSession.read().json(jsonRdd4DF);
                if(!jsonRdd4DF.isEmpty()) {
                    //JavaRDD<String> jsonRddDF = getJavaSparkContext().parallelize(jsonRdd4DF.collect());
                    Dataset<Row> json = sparkSession.read().json(jsonRdd4DF);   

                    SparkAIRMainJsonProcessor airMainJsonProcessor = new SparkAIRMainJsonProcessor();
                    airMainJsonProcessor.processAIRData(json, sparkSession);
                }

            }               
        });

        getJavaStreamingContext().start();
        getJavaStreamingContext().awaitTermination();
        getJavaStreamingContext().stop();

我们正在使用的技术:

HDFS  2.7.1.2.5 
YARN + MapReduce2  2.7.1.2.5 
ZooKeeper  3.4.6.2.5 
Ambari Infra  0.1.0 
Ambari Metrics  0.1.0 
Kafka  0.10.0.2.5 
Knox  0.9.0.2.5 
Ranger  0.6.0.2.5 
Ranger KMS  0.6.0.2.5 
SmartSense  1.3.0.0-1
Spark2  2.0.x.2.5 

我们从不同实验中得到的统计数据:

  

实验1

num_executors=6
executor_memory=8g
executor_cores=12

100个文件处理时间48分钟

  

实验2

spark.default.parallelism=12
num_executors=6
executor_memory=8g
executor_cores=12

100个文件处理时间8分钟

  

实验3

spark.default.parallelism=12
num_executors=6
executor_memory=8g
executor_cores=12

100个文件处理时间7分钟

  

实验4

spark.default.parallelism=16
num_executors=6
executor_memory=8g
executor_cores=12

100个文件处理时间10分钟

请告知我们如何处理最大值,以便排队等待。

4 个答案:

答案 0 :(得分:6)

我遇到了同样的问题,我尝试了一些尝试解决问题并得出以下结论:

首先。 Intuition表示每个执行程序必须处理一个批处理,但相反,一次只处理一个批处理,但并行处理作业和任务。

使用 spark.streaming.concurrentjobs 可以实现多批处理,但是没有记录,仍然需要一些修复。其中一个问题是保存卡夫卡的补偿。假设我们将此参数设置为4并且并行处理4个批次,如果第3个批次在第4个批次之前完成,则会提交Kafka偏移量。如果批次是独立的,则此参数非常有用。

spark.default.parallelism 因为其名称有时被视为使事物平行。但它的真正好处在于分布式随机操作。尝试不同的数字,找到一个最佳数字。您将在处理时间方面获得相当大的差异。这取决于你工作中的随机操作。将其设置得太高会降低性能。从你的实验结果中也可以看出这一点。

另一个选择是使用 foreachPartitionAsync 代替RDD上的foreach。但我认为foreachPartition更好,因为foreachPartitionAsync会排队工作,而批处理似乎会被处理,但他们的工作仍然在队列或处理中。可能是我没有正确使用它。但它在我的3项服务中表现相同。

FAIR spark.scheduler.mode 必须用于具有大量任务的作业,如将循环任务分配给作业,为较小的任务提供机会,以便在处理较大的任务时开始接收资源。

尝试调整批次持续时间+输入大小,并始终将其保持在处理持续时间以下,否则您将看到长时间积压的批次。

这些是我的发现和建议,然而,有很多配置和方法可以进行流式传输,而且通常一组操作对其他人来说并不起作用。 Spark Streaming就是学习,将您的经验和期望结合在一起,以达到一套最佳配置。

希望它有所帮助。如果有人能够具体告诉我们如何合法地并行处理批次,那将是一个很大的缓解。

答案 1 :(得分:2)

  

我们希望最大程度地实现并行性,以便不会将任何微批处理排队

关于流处理的问题是:按照收到的顺序处理数据。如果以低于到达速度的速率处理数据,它将排队。此外,不要期望一个记录的处理将突然在多个节点上并行化。

从截图中看,您的批处理时间似乎是10秒,而您的制作人在90秒内发布了100条记录。

处理2条记录需要36秒,处理17条记录需要70秒。显然,每批次都有一些开销。如果这种依赖是线性的,那么只需要4:18来处理一个小批量的所有100条记录,从而击败你的记录保持者。

由于您的代码不完整,很难说出究竟花了那么多时间。代码中的转换看起来很好但可能是动作(或后续转换)是真正的瓶颈。另外,您的代码中的任何地方都没有提及producer.flush()的内容?

答案 2 :(得分:1)

我遇到了同样的问题,我用Scala Futures解决了这个问题。

以下是一些显示如何使用它的链接:

此外,当我使用Scala Futures时,这是我的代码:

5.7

答案 3 :(得分:-1)

如果没有所有细节,很难说,但是要解决这类问题的一般建议 - 从非常简单的应用程序开始,“Hello world”类型。只需读取输入流并将数据打印到日志文件中。一旦这个工作你证明问题在应用程序中,你逐渐添加你的功能,直到你找到罪魁祸首。如果即使是最简单的应用程序也不起作用 - 您知道配置中的问题或Spark集群本身。希望这会有所帮助。