Spark 2.2引入了Kafka的结构化流媒体源。据我所知,它依靠HDFS检查点目录来存储偏移并保证"完全一次"消息传递。
但旧的Dock(如https://blog.cloudera.com/blog/2017/06/offset-management-for-apache-kafka-with-apache-spark-streaming/)表示Spark Streaming检查点无法跨应用程序或Spark升级恢复,因此不太可靠。作为一种解决方案,有一种做法是支持在支持MySQL或RedshiftDB等事务的外部存储中存储偏移量。
如果我想将Kafka源的偏移存储到事务DB,我如何从结构化流批处理获得偏移量?
以前,可以通过将RDD转换为HasOffsetRanges
:
val offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
但是使用新的Streaming API,我有一个Dataset
InternalRow
,我无法找到一种简单的方法来获取偏移量。 Sink API只有addBatch(batchId: Long, data: DataFrame)
方法,我怎么能想要获得给定批次ID的偏移?
答案 0 :(得分:26)
Spark 2.2引入了Kafka的结构化流媒体源。据我所知,它依靠HDFS检查点目录来存储偏移并保证一次"完全一次"消息传递。
正确。
每个触发器Spark Structured Streaming都会在检查点位置(使用offset
选项定义或checkpointLocation
Spark属性或随机分配)保存偏移到spark.sql.streaming.checkpointLocation
目录,以保证偏移处理最多一次。该功能称为预写日志。
检查点位置中的另一个目录是commits
目录,用于已完成的流批处理,每批具有一个文件(文件名为批处理ID)。
在Fault Tolerance Semantics中引用官方文档:
为实现这一目标,我们设计了结构化流媒体源,接收器和执行引擎,以可靠地跟踪处理的确切进度,以便通过重新启动和/或重新处理来处理任何类型的故障。假设每个流源都具有偏移(类似于Kafka偏移或Kinesis序列号)以跟踪流中的读取位置。引擎使用检查点和预写日志来记录每个触发器中正在处理的数据的偏移范围。流式接收器设计为处理重新处理的幂等功能。结合使用可重放的源和幂等接收器,结构化流可以在任何失败的情况下确保端到端的一次性语义。
每次执行触发器时StreamExecution
都会检查目录并且"计算"已经处理了什么偏移量。这样就可以至少一次语义,一次。
但旧文档(...)表示Spark Streaming检查点无法跨应用程序或Spark升级恢复,因此不太可靠。
为什么你打电话给他们"老",不在那里?
它们指的是旧的和(在我看来)死亡的Spark Streaming,它不仅保留了偏移,而且整个查询代码导致了检查点几乎无法使用的情况,例如:当你改变代码时。
时代已经结束,结构化流媒体更加谨慎,检查点的时间和时间。
如果我想将Kafka源的偏移存储到事务DB,我如何从结构化流批处理获得偏移?
解决方案可能是实现或以某种方式使用用于处理偏移检查点的MetadataLog接口。那可以工作。
如何为给定的批次ID获取偏移?
目前无法实现。
我的理解是,您将不能够这样做,因为流式语义对您来说是隐藏的。你应该不处理这种低级别的事情"称为Spark Structured Streaming用于提供一次保证的偏移量。
在Spark Summit Easy, Scalable, Fault Tolerant Stream Processing with Structured Streaming in Apache Spark的演讲中引用迈克尔·阿姆布鲁斯特的话:
你不应该推理流媒体
和further in the talk (on the next slide):
你应该写简单的查询& Spark应该不断更新答案
是一种使用StreamingQueryProgress
来限制(来自任何来源,包括Kafka)的方法,您可以使用StreamingQueryListener和onQueryProgress
回调拦截。< / p>
onQueryProgress(event:QueryProgressEvent):Unit 当有一些状态更新(更新摄取率等)时调用
使用StreamingQueryProgress
,您可以使用SourceProgress访问sources
属性,为您提供所需内容。
答案 1 :(得分:3)
相关的Spark DEV邮件列表讨论主题是here。
摘要:
Spark Streaming将支持在将来的版本中获得偏移(&gt; 2.2.0)。要遵循的JIRA门票 - https://issues-test.apache.org/jira/browse/SPARK-18258
对于Spark&lt; = 2.2.0,您可以通过从checkpoint目录中读取json来获取给定批次的偏移量(API不稳定,因此要小心):
val checkpointRoot = // read 'checkpointLocation' from custom sink params
val checkpointDir = new Path(new Path(checkpointRoot), "offsets").toUri.toString
val offsetSeqLog = new OffsetSeqLog(sparkSession, checkpointDir)
val endOffset: Map[TopicPartition, Long] = offsetSeqLog.get(batchId).map { endOffset =>
endOffset.offsets.filter(_.isDefined).map { str =>
JsonUtilsWrapper.jsonToOffsets(str.get.json)
}
}
/**
* Hack to access private API
* Put this class into org.apache.spark.sql.kafka010 package
*/
object JsonUtilsWrapper {
def offsetsToJson(partitionOffsets: Map[TopicPartition, Long]): String = {
JsonUtils.partitionOffsets(partitionOffsets)
}
def jsonToOffsets(str: String): Map[TopicPartition, Long] = {
JsonUtils.partitionOffsets(str)
}
}
此endOffset
将包含每个主题/分区的until偏移量。
获得开始偏移是有问题的,因为你必须阅读'commit'检查点目录。但通常情况下,您并不关心启动偏移,因为存储结束偏移足以让可靠的Spark作业重新启动。
请注意,您还必须将已处理的批次ID存储在您的存储中。在某些情况下,Spark可以使用相同的批次ID重新运行失败的批次,因此请确保使用最新处理的批次ID(您应该从外部存储中读取)初始化自定义接收器,并忽略任何ID为&lt; latestProcessedBatchId。顺便说一句,批处理ID在查询中不是唯一的,因此您必须分别为每个查询存储批处理ID。
答案 2 :(得分:1)
使用Kafka源的流数据集offset
为field之一。您只需查询查询中的所有偏移量并将其保存到JDBC Sink