我们正在接收来自Kafka的火花流数据。在Spark Streaming中启动执行后,它只执行一个批处理,其余批处理开始在Kafka中排队。
我们的数据是独立的,可以是并行处理。
我们尝试了多个配置,包括多个执行器,核心,背压和其他配置,但到目前为止还没有任何工作。排队的消息很多,一次只处理了一个微批处理,其余的都保留在队列中。
我们希望最大程度地实现并行性,因此我们没有足够的可用资源,因此不会对任何微批处理进行排队。那么我们如何通过最大限度地利用资源来减少时间。
// 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分钟
请告知我们如何处理最大值,以便排队等待。
答案 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解决了这个问题。
以下是一些显示如何使用它的链接:
https://alvinalexander.com/scala/how-use-multiple-scala-futures-in-for-comprehension-loop
https://www.beyondthelines.net/computing/scala-future-and-execution-context/
此外,当我使用Scala Futures时,这是我的代码:
5.7
答案 3 :(得分:-1)
如果没有所有细节,很难说,但是要解决这类问题的一般建议 - 从非常简单的应用程序开始,“Hello world”类型。只需读取输入流并将数据打印到日志文件中。一旦这个工作你证明问题在应用程序中,你逐渐添加你的功能,直到你找到罪魁祸首。如果即使是最简单的应用程序也不起作用 - 您知道配置中的问题或Spark集群本身。希望这会有所帮助。