Les RDD étendent l'interface Serialisable , ce n'est donc pas ce qui provoque l'échec de votre tâche. Cela ne signifie pas que vous pouvez sérialiser un RDD
avec Spark et éviterNotSerializableException
Spark est un moteur informatique distribué et sa principale abstraction est un ensemble de données réparties résilient ( RDD ), qui peut être considéré comme une collection distribuée. Fondamentalement, les éléments de RDD sont partitionnés sur les nœuds du cluster, mais Spark l'abstrait de l'utilisateur, permettant à l'utilisateur d'interagir avec le RDD (collection) comme s'il s'agissait d'un local.
Sans entrer dans trop de détails, mais lorsque vous exécutez différentes transformations sur un RDD ( map
, flatMap
, filter
et autres), votre code de transformation (de fermeture) est la suivante :
- sérialisé sur le nœud du pilote,
- expédiés aux nœuds appropriés du cluster,
- désérialisé,
- et enfin exécuté sur les nœuds
Vous pouvez bien sûr l'exécuter localement (comme dans votre exemple), mais toutes ces phases (à l'exception de l'expédition sur le réseau) se produisent toujours. [Cela vous permet d'attraper tous les bugs avant même le déploiement en production]
Ce qui se passe dans votre deuxième cas, c'est que vous appelez une méthode, définie en classe testing
depuis l'intérieur de la fonction map. Spark voit cela et comme les méthodes ne peuvent pas être sérialisées par elles-mêmes, Spark essaie de sérialiser la testing
classe entière , afin que le code fonctionne toujours lorsqu'il est exécuté dans une autre JVM. Vous avez deux possibilités:
Soit vous rendez les tests de classe sérialisables, donc toute la classe peut être sérialisée par 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
}
ou vous faites de la someFunc
fonction au lieu d'une méthode (les fonctions sont des objets dans Scala), afin que Spark puisse la sérialiser:
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
}
Un problème similaire, mais pas le même avec la sérialisation de classe peut vous intéresser et vous pouvez le lire dans cette présentation Spark Summit 2013 .
Comme une note de côté, vous pouvez réécrire rddList.map(someFunc(_))
à rddList.map(someFunc)
, ils sont exactement les mêmes. Habituellement, le second est préféré car il est moins bavard et plus propre à lire.
EDIT (2015-03-15): SPARK-5307 a introduit SerializationDebugger et Spark 1.3.0 est la première version à l'utiliser. Il ajoute un chemin de sérialisation à une exception NotSerializableException . Lorsqu'une exception NotSerializableException est rencontrée, le débogueur visite le graphique d'objet pour trouver le chemin vers l'objet qui ne peut pas être sérialisé et construit des informations pour aider l'utilisateur à trouver l'objet.
Dans le cas d'OP, c'est ce qui est imprimé sur 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>)