好吧,所以我问过一个与Spark如何在内部处理异常有关的类似question,但我当时的例子并不是很清楚或完整。那里的答案指向了某个方向,但我无法解释一些事情。
我已经设置了一个虚拟火花流媒体应用程序,在转换阶段,我有一个俄语轮盘表达式,可能会或不会抛出异常。如果抛出异常,我会停止Spark流式上下文。就是这样,没有其他逻辑,没有RDD
转换。
object ImmortalStreamingJob extends App {
val conf = new SparkConf().setAppName("fun-spark").setMaster("local[*]")
val ssc = new StreamingContext(conf, Seconds(1))
val elems = (1 to 1000).grouped(10)
.map(seq => ssc.sparkContext.parallelize(seq))
.toSeq
val stream = ssc.queueStream(mutable.Queue[RDD[Int]](elems: _*))
val transformed = stream.transform { rdd =>
try {
if (Random.nextInt(6) == 5) throw new RuntimeException("boom")
else println("lucky bastard")
rdd
} catch {
case e: Throwable =>
println("stopping streaming context", e)
ssc.stop(stopSparkContext = true, stopGracefully = false)
throw e
}
}
transformed.foreachRDD { rdd =>
println(rdd.collect().mkString(","))
}
ssc.start()
ssc.awaitTermination()
}
在IntelliJ中运行它会在某个时刻抛出异常。有趣的部分:
RDD
后抛出异常,应用程序会在打印错误消息后挂起并且永不停止,这不是我想要的为什么应用程序会挂起而不是在第二种情况下死亡?
我在Scala 2.11.8上运行Spark 2.1.0。获取try-catch解决了问题(Spark自行停止)。此外,移出foreachRDD
内的try-catch解决了这个问题。
然而,我正在寻找一个可以帮助我理解这个特定例子中发生了什么的答案。
答案 0 :(得分:4)
您只会在操作中看到异常(例如本例中为foreachRDD
)而不是转换(在本例中为transform
),因为操作会延迟执行转换。这意味着您的转换甚至会在行动之前发生。这是必要的原因要求改变分布式处理如何工作的心理模型。
考虑传统的单线程程序。代码逐行进行,如果抛出异常而不处理,后续的代码行就不会执行。在分布式系统中,相同的Spark转换在多台计算机上并行运行(和以不同的速度运行),抛出异常时会发生什么?它不是那么简单,因为一台机器上的异常与其他机器上的代码无关,这就是你想要的。想要在整个集群中传播的所有独立任务只是关闭异常只是单机思维,而不是转换为分布式范例。司机应该怎么处理?
根据Matei Zaharia的说法,现在是Databricks和Spark在Berkeley的创建者之一," 异常应该被发送回驱动程序并记录在那里(抛出SparkException
如果任务失败超过4次)。" (顺便提一下,可以使用spark.task.maxFailures
更改此默认重试次数。)。因此,如果在执行程序上正确配置了Log4J,那么将在那里记录异常;然后它将被序列化并发送回驱动程序,默认情况下将再次尝试3 更多次。
在你的特殊情况下,我猜你会发生一些事情。首先,您在一台计算机上运行,这将给出异常处理在分布式模型中如何工作的误导性图片。其次,你过早地停止了上下文。停止上下文is an extremely destructive operation,其中包括停止所有听众和DAGScheduler
。坦率地说,当你基本上把灯关掉时,我不知道你怎么能指望Spark把所有东西整齐地包起来。
最后,我要提到更优雅的异常处理模型可能正在Try
内执行转换。由于您的转换将返回RDD[Try[T]]
或DStream[Try[T]]
,这意味着您将需要为每个元素处理Success
和Failure
个案例,因此最终可能会出现更麻烦的代码。但是,您将能够在下游传播成功和错误信息以及monad提供的所有好处,包括映射RDD[Try[A]] => RDD[Try[B]]
甚至使用for
理解(凭借flatMap
)。
答案 1 :(得分:2)
首先,将异常处理移动到foreachRDD时它起作用的原因是因为foreachRDD主体中的代码是异常显示的位置。当调用动作(例如,收集)时,懒惰地评估变换。因此,捕获错误后,正确调用ssc.stop会中止作业,ssc在范围内,一切都很好且确定性。
现在,当您在转换中调用ssc.stop时,事情会变得更加混乱。上下文实际上并不在转换主体的范围内,因为转换是由工作者而不是驱动程序执行的。它恰好恰好适合你,因为你在本地跑步都很好。移动到分布式环境后,您将发现您的作业无法运行,因为您无法将上下文分发给执行程序。
当从转换中抛出异常时,堆栈跟踪将被发送到驱动程序,驱动程序将重试该任务,但在您的情况下,您已停止上下文,那么谁知道将要发生什么?我想如果在第一个RDD上发生异常,那么这个工作就会被杀死,因为它很快就会被杀死,无论是通过侥幸还是设计。而且我猜想如果异常不在第一个RDD转换中就不会全部被杀死,因为你停止上下文并以某种方式创建一个驱动程序和/或任务无限期等待的条件。
答案 2 :(得分:0)
过去几周我在这个问题上进行了挖掘,所以我发现让你们了解过去几天借助StackOverflow社区和Apache Spark社区学到的东西会很高兴:
$(document).ready(function () {
$.ajax({
url: '/api/UserApi/',
type: 'GET',
contentType: "application/json;charset=utf-8",
dataType: "json",
success: function (data) {
alert('User Added Successfudly');
for (var i = 0; i < data.length; i++) {
$("#absa").append("<tr><td>" + data[i].user_Name + "</td><td>" + data[i].Address + "</td><td>" + data[i].Email + "</td><td>" + data[i].MobileNo + "</td><td><button onclick="update()"/></td></tr>");
}
},
error: function () { alert('User not Added'); }
});
});
。如果您想这样做,只需在单独的thread中进行。否则你可能偶然发现死锁或一些未定义的行为。谢谢你@Shinzong Xhu。StreamingContext
,其中可以包含您自己的异常。你的流媒体应用程序不会死,但至少流媒体任务会失败。感谢@Vidya对此的启发。更新[不要忘记关闭外部资源]
请务必查看是否需要停止与Spark无关的任何其他资源。例如,如果您停止流式传输作业但驱动程序使用来自akka的SparkException
来执行某些HTTP呼叫,请不要忘记终止系统或您的应用将挂起。这似乎与Spark无关,但不要忘记它。
再次感谢其他答案,我希望您会发现此信息有用。