我一直在与org.apache.spark.SparkException: Task not serializable
挣扎,但最终想出了如何使它起作用的方法:
case class Article(id: Int, title: String, content: String) extends Serializable
val index: RDD[(String, List[(Int, Int)])] = (for {
article <- articlesRDD
text = article.title + article.content
word <- text.split(" ")
} yield (word, (article.id, 1)))
.groupByKey()
.mapPartitions{
_.map {
case(k, v) => (k, v.groupBy(_._1).map(pair => (pair._1, pair._2.map(_._2).sum)).toList) // Works as expected
//case(k, v) => (k, reducer(v.toList)) // Fails
}
}.cache()
这是reducer
:
def reducer(list: List[(Int, Int)]): List[(Int, Int)] = {
list.groupBy(_._1).map(
pair => (pair._1, pair._2.map(_._2).sum)
).toList
}
我也尝试将reducer
函数定义为val
,但遇到相同的错误。实际上,该错误发生在Databricks笔记本中,在我的计算机上以本地模式运行Spark时,它运行正常。
为什么注释的case
语句失败?
即使它们不像我的reducer
函数那样琐碎,我也必须始终传递匿名函数吗?
先谢谢您了:)
答案 0 :(得分:1)
您没有说在哪里 reducer
被定义,但是它很可能属于不可序列化的类(例如,包含SparkContext
的类)。然后,使用它需要捕获调用它的实例。改为在object
中定义它。
Spark的API在很大程度上依赖于驱动程序中传递函数来在集群上运行。有两种推荐的方法可以做到这一点:
- 匿名函数语法,可用于简短的代码段。
全局单例对象中的静态方法。例如,您可以定义
object MyFunctions
,然后传递MyFunctions.func1
,如下所示:object MyFunctions { def func1(s: String): String = { ... } } myRdd.map(MyFunctions.func1)
请注意,虽然也可以在类实例(而不是单例对象)中传递对方法的引用,但这需要将包含该类的对象与方法一起发送。