我不认为其他答案是完全正确的 . RDDs are indeed serializable,所以这不是导致任务失败的原因 .
Spark是一个分布式计算引擎,它的主要抽象是一个弹性分布式数据集( RDD ),可以看作是一个分布式集合 . 基本上,RDD的元素在集群的节点之间进行分区,但Spark将其从用户抽象出来,让用户与RDD(集合)进行交互,就好像它是本地的一样 .
不要涉及太多细节,但是当你在RDD( map , flatMap , filter 和其他)上运行不同的转换时,你的转换代码(闭包)是:
在驱动程序节点上序列化,
发送到集群中的相应节点,
反序列化,
并最终在节点上执行
您当然可以在本地运行(如您的示例所示),但所有这些阶段(除了通过网络传输)仍然会发生 . [这使您可以在部署到 生产环境 之前捕获任何错误]
在第二种情况下会发生的情况是,您正在调用一个方法,该方法在map函数内部的 testing 类中定义 . Spark看到了这一点,并且由于方法无法自行序列化,因此Spark会尝试序列化整个 testing 类,以便代码在另一个JVM中执行时仍然可以工作 . 你有两种可能性:
要么使类测试可序列化,所以Spark可以序列化整个类:
import org.apache.spark.{SparkContext,SparkConf}
object Spark {
val ctx = new SparkContext(new SparkConf().setAppName("test").setMaster("local[*]"))
}
object NOTworking extends App {
new Test().doIT
}
class Test extends java.io.Serializable {
val rddList = Spark.ctx.parallelize(List(1,2,3))
def doIT() = {
val after = rddList.map(someFunc)
after.collect().foreach(println)
}
def someFunc(a: Int) = a + 1
}
或者您使用 someFunc 函数而不是方法(函数是Scala中的对象),以便Spark能够序列化它:
import org.apache.spark.{SparkContext,SparkConf}
object Spark {
val ctx = new SparkContext(new SparkConf().setAppName("test").setMaster("local[*]"))
}
object NOTworking extends App {
new Test().doIT
}
class Test {
val rddList = Spark.ctx.parallelize(List(1,2,3))
def doIT() = {
val after = rddList.map(someFunc)
after.collect().foreach(println)
}
val someFunc = (a: Int) => a + 1
}
作为旁注,您可以将 rddList.map(someFunc(_)) 重写为 rddList.map(someFunc) ,它们完全相同 . 通常,第二种是优选的,因为它不那么冗长和清晰 .
编辑(2015-03-15):SPARK-5307介绍 SerializationDebugger 和Spark 1.3.0是第一个使用它的版本 . 它添加了NotSerializableException的序列化路径 . 遇到NotSerializableException时,调试器访问对象图以查找无法序列化的对象的路径,并构造信息以帮助用户查找对象 .
在OP的情况下,这是打印到stdout的内容:
Serialization stack:
- object not serializable (class: testing, value: testing@2dfe2f00)
- field (class: testing$$anonfun$1, name: $outer, type: class testing)
- object (class testing$$anonfun$1, )