我正在使用Spark结构化流传输来读取Kafka主题中的记录;我打算计算Spark readstream
这是一个片段:
val kafka_df = sparkSession
.readStream
.format("kafka")
.option("kafka.bootstrap.servers", "host:port")
.option("subscribe", "test-count")
.load()
我从文档中了解到,当启动streamingQuery
(下一个)时,kafka_df将被延迟评估,并且在评估时,它会保留一个微型批处理。因此,我认为在 topic 上先进行groupBy
,然后再进行count
即可。
赞:
val counter = kafka_df
.groupBy("topic")
.count()
现在要评估所有这些,我们需要一个streaminQuery(可以说是一个控制台接收器查询)以将其打印在控制台上。这就是我看到问题的地方。 aggregate
数据帧(例如kafka_df
)上的streamingQuery仅适用于outputMode
完成/更新 ,而不适用于 附加 。
这实际上意味着,streamingQuery报告的计数是累积的。
赞:
val counter_json = counter.toJSON //to jsonify
val count_query = counter_json
.writeStream.outputMode("update")
.format("console")
.start() // kicks of lazy evaluation
.awaitTermination()
在受控设置中,其中:
实际已发布记录:1500
实际收到的微型批次:3
a实际收到的记录:1500
每个微批处理的数量应该为 500 ,所以我希望(希望)查询能够打印到控制台:
topic:测试计数
数量:500
主题:测试计数
数量:500
主题:测试计数
数:500
但事实并非如此。它实际上会打印:
topic:测试计数
数量:500
主题:测试计数
数量:1000
主题:测试计数
数:1500
我了解这是由于'outputMode'完成/更新(累积)
我的问题:是否可以准确获取Spark-Kafka结构化流的每个微批计数?
从文档中,我发现了有关水印方法(以支持追加):
val windowedCounts = kafka_df
.withWatermark("timestamp", "10 seconds")
.groupBy(window($"timestamp", "10 seconds", "10 seconds"), $"topic")
.count()
val console_query = windowedCounts
.writeStream
.outputMode("append")
.format("console")
.start()
.awaitTermination()
但是此console_query
的结果不准确,并且显示出来是不正确的。
TL; DR-对于准确计数Spark-Kafka微批处理中的记录的任何想法,将不胜感激。
答案 0 :(得分:1)
如果要使用Kafka在结构化流应用程序中使用每个触发器仅处理特定数量的记录,请使用选项maxOffsetsPerTrigger
val kafka_df = sparkSession
.readStream
.format("kafka")
.option("kafka.bootstrap.servers", "host:port")
.option("subscribe", "test-count")
.option("maxOffsetsPerTrigger", 500)
.load()
答案 1 :(得分:0)
“ TL; DR-关于准确计数Spark-Kafka微批处理中的记录的任何想法,将不胜感激。”
您可以使用StreamingQueryListener
(ScalaDocs)计算从Kafka获取的记录。
这使您可以打印出从已订阅的Kafka主题收到的确切行数。 onQueryProgress
API在每个微批处理期间都会被调用,并且包含有关查询的很多个有用的元信息。如果没有数据流入查询,则每10秒调用一次onQueryProgress。下面是一个简单的示例,它打印出输入消息的数量。
spark.streams.addListener(new StreamingQueryListener() {
override def onQueryStarted(queryStarted: QueryStartedEvent): Unit = {}
override def onQueryTerminated(queryTerminated: QueryTerminatedEvent): Unit = {}
override def onQueryProgress(queryProgress: QueryProgressEvent): Unit = {
println("NumInputRows: " + queryProgress.progress.numInputRows)
}
})
如果您要验证结构化流查询的性能,通常最好留意以下两个指标:
queryProgress.progress.inputRowsPerSecond
queryProgress.progress.processedRowsPerSecond
如果输入高于已处理,则可能会增加工作资源或降低最大限制(通过减少readStream选项maxOffsetsPerTrigger)。如果处理得更高,则可能要增加此限制。