Os RDDs estendem a interface Serialisable , portanto, não é isso que está causando a falha da sua tarefa. Agora, isso não significa que você pode serializar um RDD
com o Spark e evitarNotSerializableException
O Spark é um mecanismo de computação distribuído e sua abstração principal é um conjunto de dados distribuído resiliente ( RDD ), que pode ser visto como uma coleção distribuída. Basicamente, os elementos do RDD são particionados nos nós do cluster, mas o Spark abstrai isso do usuário, permitindo que ele interaja com o RDD (coleção) como se fosse um local.
Não entrar em muitos detalhes, mas quando você executar transformações diferentes em um RDD ( map
, flatMap
, filter
e outros), o código de transformação (fechamento) é:
- serializado no nó do driver,
- enviado para os nós apropriados no cluster,
- desserializado,
- e finalmente executado nos nós
Obviamente, você pode executá-lo localmente (como no seu exemplo), mas todas essas fases (além do envio pela rede) ainda ocorrem. [Isso permite detectar erros antes de implantar na produção]
O que acontece no seu segundo caso é que você está chamando um método, definido na classe testing
de dentro da função de mapa. O Spark vê que, e como os métodos não podem ser serializados por conta própria, o Spark tenta serializar toda a testing
classe, para que o código ainda funcione quando executado em outra JVM. Você tem duas possibilidades:
Você pode tornar o teste de classe serializável, para que toda a classe possa ser serializada pelo 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 você cria uma someFunc
função em vez de um método (funções são objetos no Scala), para que o Spark possa serializá-lo:
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
}
Semelhante, mas não o mesmo problema com a serialização de classe, pode ser do seu interesse e você pode ler sobre isso nesta apresentação do Spark Summit 2013 .
Como observação lateral, você pode reescrever rddList.map(someFunc(_))
para rddList.map(someFunc)
, eles são exatamente os mesmos. Geralmente, o segundo é preferido, pois é menos detalhado e limpo de ler.
EDIT (2015-03-15): O SPARK-5307 introduziu o SerializationDebugger e o Spark 1.3.0 é a primeira versão a usá-lo. Ele adiciona o caminho de serialização para um NotSerializableException . Quando uma NotSerializableException é encontrada, o depurador visita o gráfico do objeto para encontrar o caminho para o objeto que não pode ser serializado e constrói informações para ajudar o usuário a encontrar o objeto.
No caso do OP, é isso que é impresso no 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>)