我的团队现在正在进入结构化流媒体领域。我是结构化流媒体的新手。
我有
的要求来源 - CSV
接收器 - JSON
集群:Spark 2.2.1
编程语言:Scala
构建工具:Gradle
我已经实现了这个简单的代码
val schema = StructType(
Array(StructField("customer_id", StringType),
StructField("name", StringType),
StructField("pid", StringType),
StructField("product_name", StringType)))
val fileData = spark.readStream
.option("header", "true")
.schema(schema)
.csv(args(0))
然后我将一个简单的聚合应用为
// The actual business logic is more complex than this
val customerCount = fileData.groupBy("customer_id").count()
最后,写信给JSON
val query = customerCount
.writeStream
.format("json")
.option("path", "src/main/resources/output/myjson_dir")
.option("checkpointLocation", "src/main/resources/chkpoint_dir")
.trigger(Trigger.ProcessingTime("10 seconds"))
.start()
.format("console")
时,这可以正常工作。但是当我使用.format("json")
- 线程“main”中的异常org.apache.spark.sql.AnalysisException:当没有水印的流式DataFrames / DataSets上有流式聚合时,不支持追加输出模式;; 聚合[customer_id#0],[customer_id#0,count(1)AS count#18L] + - StreamingRelation DataSource(org.apache.spark.sql.SparkSession @ 4b56b031,csv,List(),Some(StructType(structField(customer_id,StringType,true),StructField(name,StringType,true),StructField(product_id,StringType) ,true),StructField(product_name,StringType,true))),List(),None,Map(header - > true,path - > / Users / Underwood / Documents / workspace / Spark_Streaming_Examples / src / main / resources /输入),无),FileSource [/ Users / Underwood / Documents / workspace / Spark_Streaming_Examples / src / main / resources / input],[customer_id#0,name#1,product_id#2,product_name#3,date#4] at org.apache.spark.sql.catalyst.analysis.UnsupportedOperationChecker $ $ .ORG阿帕奇$ $火花SQL $ $催化剂分析$ $$ UnsupportedOperationChecker throwError(UnsupportedOperationChecker.scala:297)
我尝试过outputMode = "update"
和outputMode = "complete"
的其他组合。但这些也会引发错误。
为什么会这样?这是预期的行为吗?如何将输出写入JSON接收器?
以上Exception讨论了如何使用水印。 AFAIK,水印与Timestamp字段一起使用,但我的输入数据中没有时间戳或日期字段。如果我错了,请告诉我。如何在这里添加水印有所作为?
我的下一次尝试是编写自定义ForEachSink。我参考了这个post。但这对我也没有帮助。这里的问题是,我得到200个目录,每个目录中都有0字节文件。
如何在最终输出中选择非分组?在简单的批处理中,我通常通过将聚合的DF与原始DF连接并选择所需的行来实现此目的。但结构化流媒体似乎不喜欢这种方法。这是我的示例代码段
val customerCount = fileData.groupBy("customer_id").count()
val finalDF = fileData.join(customerCount, Seq("customer_id"))
.select("customer_id", "count", "product_name" )
请告诉我,如果我遗漏了任何行为。
答案 0 :(得分:2)
阅读官方的Spark结构化流文档related to watermarks。
基本上,当您进行汇总时,必须设置outputMode = "complete"
,因为在不将以前完成的处理(例如字数统计)保留在内存中的情况下添加新数据是没有意义的。
因此,您必须使用水印或窗口函数指定程序何时必须开始新的聚合以及何时数据太迟。
如果没有带有时间戳的列,则可以使用now()
函数创建一个列,这就是处理时间。
如果有任何不清楚或有疑问的地方,请发表评论,我会更新答案。