我正在从Spark Streaming迁移到Structured Streaming,我遇到以下代码的问题:
def processDataSet(inputDataset: Dataset[MyMessage], foobar: FooBar) = {
inputDataset.foreachPartition { partitionIterator =>
val filteredIterator = partitionIterator.filter(foobar.filter)
...
...
}
}
val streamingQuery = inputDataset
.writeStream
.trigger(ProcessingTime("5 seconds"))
.outputMode("append")
.format("console")
.start
使用以下AnalysisException
错误输出:
引起:org.apache.spark.sql.AnalysisException:必须使用writeStream.start();;
执行带有流源的查询
流式查询是否不支持foreachPartition
? writeStream.foreach
是在这种情况下实施foreachPartition
的唯一方法吗?
我希望避免发送每个事件,而是累积所有行,形成一个巨大的POST请求主体并将其发送到HTTP端点。因此,如果批处理中的1000个事件和5个分区,则在每个请求正文中并行生成5个请求和200个事件。
答案 0 :(得分:2)
TL; DR 是的。不支持foreachPartition
操作,您应该使用ForeachWriter。
引用foreachPartition的scaladoc:
foreachPartition(f:(Iterator [T])⇒Unit):Unit 将函数
f
应用于此数据集的每个分区。
正如您现在可能已经发现的那样,foreach
是一个操作,因此会触发Spark执行。
由于您使用流数据集,因此不允许使用foreach
等“传统”方法触发执行。
引用结构化流媒体Unsupported Operations:
此外,有些数据集方法不适用于流式数据集。它们是立即运行查询并返回结果的操作,这对流式数据集没有意义。相反,这些功能可以通过显式启动流式查询来完成(请参阅下一节)。
流媒体替代方案中有foreach
运算符(也称为接收器)。这就是结构化流媒体中的foreachPartition
。
foreach操作允许对输出数据计算任意操作。
要使用它,您必须实现接口
ForeachWriter
,该接口具有在触发器后生成一系列行作为输出时被调用的方法。
我希望避免发送每个事件,而是累积所有行,形成一个巨大的POST请求主体并将其发送到HTTP端点。因此,如果批处理中的1000个事件和5个分区,则在每个请求正文中并行生成5个请求和200个事件。
在将数据集写入接收器之前,这似乎是一个聚合,不是吗?使用groupBy
运算符和collect_list
函数对行进行分组,这样当您writeStream
时,您将拥有任意数量的组。
除非没有别的办法,否则我宁愿避免使用称为分区的RDD这种低级功能作为优化写入的方法。