我试图了解如何使Spark Streaming应用程序更容错(特别是在尝试写入下游依赖项时),并且我不知道在尝试中处理失败的最佳方法是什么将结果写入外部源,如Cassandra,DynamoDB等。
例如,我有一个Spark Streaming作业从Stream(Kafka,Flume等等)中提取数据......我还没有最终确定使用哪种技术,将类似的项目聚合在一起,然后将结果写入外部商店。 (即Cassandra,DynamoDB或任何正在接收我的DStream计算结果的内容)。
我试图弄清楚如何处理外部依赖关系无法写入的情况。也许集群发生故障,可能存在权限问题等,但我的工作无法将结果写入外部依赖项。有没有办法暂停Spark Streaming以便接收者不再继续批量处理数据?我应该只是睡觉当前批次并让接收器继续存储批次吗?如果问题是暂时的(几秒钟),继续批处理可能是可以接受的,但如果依赖性下降几分钟或1小时以上会发生什么?
我有一个想法是让一个监视进程在后台监视依赖项的健康状况,如果它发现它不健康"它将停止这项工作。然后,当所有依赖项都运行正常时,我可以重新启动作业并处理未写入外部源的所有数据。
我的另一个想法是以某种方式在DStream forEachRdd方法中发出信号,表示存在问题。是否有某种异常,我可以在DStream中抛出,它会向驱动程序发出信号,告知它应该停止?
如果有人对如何处理外部容错有任何经验,或者可以指出我的好文章/视频,那就太棒了。
由于
答案 0 :(得分:4)
我相信这里没有简单而通用的答案。很大程度上取决于应用语义,数据源类型(可靠接收器,可靠接收器,基于文件,无接收器)和要求。
通常,您不应该让应用程序因单个IO故障而失败。假设你有一些动作:
outputAction[T](rdd: RDD[T]): Unit = ???
至少确保它不会向您的驱动程序传播异常。
outputActionWithDelay[T](d: Duration)(rdd: RDD[T]) = ???
stream foreachRDD { rdd => Try(outputAction(rdd)) }
问题仍然是下一步。你能做的最简单的事情就是放弃给定的窗口。根据应用程序,它可以是可接受的解决方案,但通常有很多情况下丢失一些数据是完全可以接受的。
如果达到某个阈值,可以通过跟踪故障并采取其他一些措施来进一步改进。
如果丢弃数据不可接受,则下一步是在延迟一段时间后重试:
outputActionWithDelay[T](d: Duration)(rdd: RDD[T]) = ???
stream foreachRDD {
rdd => Try(outputAction(rdd))
.recoverWith { case _ => Try(outputActionWithDelay(d1)(rdd)) }
.recoverWith { case _ => Try(outputActionWithDelay(d2)(rdd)) }
...
}
重试次数和延迟持续时间因具体情况而异,并且会依赖于源和存储传入数据的能力。
当我们上次重试时你能做什么?对于初学者,我们可以添加替代输出源。您可以将所有内容推送到可靠的外部文件存储中,而不是使用主源,而不用担心它。如果输出源需要特定的输入数据顺序,则可能不适用,但是否则值得尝试。
alternativeOutputAction[T](rdd: RDD[T]) = ???
stream foreachRDD {
rdd => Try(outputAction(rdd))
.recoverWith { case _ => Try(outputActionWithDelay(d1)
...
.recoverWith { case _ => Try(outputActionWithDelay(dn)(rdd)) }
.recoverWith { case _ => Try(alternativeOutputAction(rdd))
}
如果失败,可能是严重问题的症状,我们在应用程序级别上做的不多。我们可以回到第一种方法,只希望情况很快得到解决,或者选择更复杂的方法。
如果输入源可以缓冲数据并且我们使用可靠的存储和复制,那么我们可以enable checkpointing并简单地终止应用程序。
如果您尝试恢复它可能是一个好主意添加CircuitBreaker的某个变体,如果应用程序遇到多个失败尝试无延迟地达到主输出丢弃恢复尝试。
答案 1 :(得分:1)
现在我使用直接流并自行保存偏移量。这可能无法解决您的问题,至少在您发现外部存储存在问题后,您可以从停止的位置重新启动。