RDD扩展了Serialisable接口,所以这不是导致您的任务失败的原因。现在,这并不意味着您可以RDD
使用Spark 序列化并避免NotSerializableException
Spark是一个分布式计算引擎,其主要抽象是可恢复的分布式数据集(RDD),可以将其视为分布式集合。基本上,RDD的元素在群集的各个节点之间进行分区,但是Spark将其从用户那里抽象出来,让用户可以与RDD(集合)进行交互,就好像它是本地的一样。
不要让太多细节,但是当你在一个RDD(运行不同的变换map
,flatMap
,filter
等),您的转换代码包(closure)是:
- 在驱动程序节点上序列化,
- 运送到群集中的适当节点,
- 反序列化
- 最后在节点上执行
您当然可以在本地运行此程序(如您的示例中所示),但是所有这些阶段(除了通过网络传输外)仍然会发生。[这使您甚至可以在部署到生产之前捕获任何错误]
在第二种情况下,您正在调用的是testing
从map函数内部在类中定义的方法。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
}
类序列化的相似但不相同的问题可能引起您的兴趣,您可以在此Spark Summit 2013演示文稿中进行阅读。
另外,您可以重写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, <function1>)