我读了一些关于这个主题的相关问题,但仍然无法理解以下内容。我有这个简单的Spark应用程序,它从文件中读取一些JSON记录:
object Main {
// implicit val formats = DefaultFormats // OK: here it works
def main(args: Array[String]) {
val conf = new SparkConf().setMaster("local").setAppName("Spark Test App")
val sc = new SparkContext(conf)
val input = sc.textFile("/home/alex/data/person.json")
implicit val formats = DefaultFormats // Exception: Task not serializable
val persons = input.flatMap { line ⇒
// implicit val formats = DefaultFormats // OK: here it also works
try {
val json = parse(line)
Some(json.extract[Person])
} catch {
case e: Exception ⇒ None
}
}
}
}
我认为隐式formats
不可序列化,因为它包含一些ThreadLocal
的日期格式。但是,为什么它作为object Main
的成员或flatMap
的关闭内部而不是作为val
函数中的常见main
放置?
提前致谢。
答案 0 :(得分:2)
如果formats
在flatMap
内,则它仅作为执行映射功能的一部分创建。因此映射器可以序列化并发送到集群,因为它还不包含formats
。另一方面,每次映射器运行时都会重新创建formats
(即每行一次) - 您可能更喜欢使用mapPartitions
而不是flatMap
,这样您就可以获得值为每个分区创建一次。
如果formats
超出flatMap
,那么它会在主计算机上创建一次,并且您正在尝试将其序列化并将其发送到群集。
我不明白为什么formats
作为Main
的字段可行。也许object
是神奇的伪序列化,因为它们是单例(即它们的字段实际上并不是序列化的,而是这是对单个静态Main
实例的引用被序列化的事实)?这只是一个猜测。
答案 1 :(得分:1)
回答你的问题的最佳方式我认为有三个简短的答案:
1)为什么它作为主对象的成员放置时工作?,这里的问题是代码可以工作,因为它在一个Object内部,而不是主对象。现在:为什么?因为Spark序列化整个对象并将其发送给每个执行程序,而且Scala中的Object生成类似于JAVA Static类,Java类中的静态字段的初始值存储在jar中,工作人员可以直接使用它。如果您使用类而不是Object,则不一样。
2)第二个问题是:如果它在平面图中,它为什么会起作用?。 当您在RDD(过滤器,flatMap等)上运行转换时,您的转换代码是:在驱动程序节点上序列化,发送给worker,一旦它将被反序列化并执行。正如你可以看到的完全相同,1)代码将被“自动化”序列化。
最后问题:为什么这不作为main函数中的公共val?这是因为val没有“自动”序列化,但你可以像这样测试它:val yourVal = new yourVal with Serializable