我有一个用Scala编写的Spark流媒体应用程序,运行在CDH中。该应用程序从Kafka读取数据,并将数据写入HDFS。在将数据写入HDFS之前,我先执行partitionBy,以便将数据写入分区。这是代码:
//Some code
val ssc = new StreamingContext(sc, Seconds(1))
val stream = KafkaUtils.createDirectStream[String, String](
ssc,
PreferConsistent,
Subscribe[String,String](topics, kafkaParams))
val sparkExecutorsCount = sc.getConf.getInt("spark.executor.instances", 1)
//Some code
stream.foreachRDD { rdd =>
if(!rdd.isEmpty()) {
val offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
val data = rdd.map(kafkaData => (getKey(kafkaData.value()), kafkaData.value()))
val columns = Array("key", "value")
val addOp = (record1: String, record2:String) => record1 + "\n" + record2
val mergeOp = (record1: String, record2:String) => record1 + record2
val initialValue = ""
val out = data.aggregateByKey(initialValue)(addOp, mergeOp)
out.toDF(columns: _*).coalesce(sparkExecutorsCount)
.write.mode(SaveMode.Append)
.partitionBy("key").text(MY_PATH)
stream.asInstanceOf[CanCommitOffsets].commitAsync(offsetRanges)
} else {
//handle empty RDD
}
}
我希望此代码生成以下输出(ls -l
命令的示例):
> MY_PATH/key=1
> MY_PATH/key=1/file1.txt
> MY_PATH/key=1/file2.txt
> MY_PATH/key=1/file3.txt
> MY_PATH/key=2
> MY_PATH/key=2/file1.txt
> MY_PATH/key=2/file2.txt
> MY_PATH/key=2/file3.txt
,并且在每个文本文件中,将逐行从DataFrame中输入条目。
实际上,这实际上正在发生。唯一的问题是,即使我initialValue
,initalValue=""
始终在每个文件中都显示为第一行,因此,每个文件中总是出现多余的空行。
这个多余的空行对我来说是个大问题,我必须避免。一种选择是使用groupByKey
而不是aggregateByKey
,但是groupByKey
会导致群集中出现更多改组,我想避免这种情况。
请告知如何防止每个书面文件中多余的空行。
答案 0 :(得分:0)
TL; DR 只需使用groupByKey
,然后使用mapValues(_.mkString("\n"))
。
两件事:
initialValue
可以被添加任意次(实际上是#partitions次)。这意味着每个分区都将以空字符串开头,后跟换行符。您检查record1
和record2
的{{1}}或addOp
是否为空,如果为空,则跳过mergeOp
。
另外,您的声明:
但是groupByKey会在集群中引起更多改组,我想避免这种情况。
不太准确。您拥有的代码不会(如果有的话)大大减少数据量。根据键的不同,它实际上可以将其增大。
例如参见