当我写RDD转换时,例如
val rdd = sc.parallelise(1 to 1000)
rdd.map(x => x * 3)
我理解只是一个Function1的闭包(x => x * 3
)需要是Serializable并且我想我在某处读到了 编辑:它正好在文档中暗示: http://spark.apache.org/docs/latest/programming-guide.html#passing-functions-to-spark 它被“发送”给工人执行。 (例如,Akka向线路工作人员发送“可执行的一段代码”)
这是怎么回事?
有人参加了聚会,我参加了评论并说它实际上并没有发送任何序列化的代码,但是因为每个工作者都得到了jar的“副本”,所以它只需要引用哪个函数来运行或类似这样的东西(但我不确定我是否正确引用了那个人)
我现在对它的实际运作方式感到十分困惑。
所以我的问题是
如何向工人发送转型关闭?通过akka序列化?或者他们“已经在那里”,因为火花将整个超级罐发送给每个工人(听起来不太可能......)
如果是这样,那么罐子的其余部分如何发送给工人?这是“cleanupClosure”在做什么?例如只发送相关的字节码给工人而不是整个uberjar? (例如,只有关闭的依赖代码?)
总而言之,在任何时候都会引发--jars
类路径中的jar与工作者的某种程度的同步?或者它是否向工人发送“恰当数量”的代码?如果确实发送了闭包,是否需要重新计算它们?或者每次安排任务时是否发送关闭任务?对不起,如果这是愚蠢的问题,但我真的不知道。
如果可以提供答案,请添加来源,我在文档中找不到它,我太谨慎了,只是通过阅读代码来尝试结束它。
答案 0 :(得分:2)
闭包在运行时肯定是序列化的。我有很多实例在运行时看到Closure Not Serializable异常 - 来自pyspark和scala。有一个叫做
的复杂代码来自ClosureCleaner.scala
def clean(
closure: AnyRef,
checkSerializable: Boolean = true,
cleanTransitively: Boolean = true): Unit = {
clean(closure, checkSerializable, cleanTransitively, Map.empty)
}
试图缩小被序列化的代码。然后通过线路发送代码 - 如果它是可序列化的。否则将抛出异常。
以下是ClosureCleaner的另一段摘录,用于检查序列化传入函数的能力:
private def ensureSerializable(func: AnyRef) {
try {
if (SparkEnv.get != null) {
SparkEnv.get.closureSerializer.newInstance().serialize(func)
}
} catch {
case ex: Exception => throw new SparkException("Task not serializable", ex)
}
}