什么时候可以序列化Spark任务?

时间:2015-03-09 04:44:32

标签: scala serialization closures apache-spark

我读了一些关于这个主题的相关问题,但仍然无法理解以下内容。我有这个简单的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放置?

提前致谢。

2 个答案:

答案 0 :(得分:2)

如果formatsflatMap内,则它仅作为执行映射功能的一部分创建。因此映射器可以序列化并发送到集群,因为它还不包含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