RDD розширюють інтерфейс Serialisable , тому це не те, що спричиняє збій вашої задачі. Тепер це не означає, що ви можете серіалізувати RDD
іскру і уникатиNotSerializableException
Spark - це двигун розподілених обчислень, а його основна абстракція - це стійкий розподілений набір даних ( RDD ), який можна розглядати як розподілену колекцію. В основному елементи RDD розподілені по вузлах кластера, але Spark відводить це від користувача, дозволяючи користувачеві взаємодіяти з RDD (колекцією) так, ніби це локальний.
Щоб не потрапити в занадто багато деталей, але при запуску різних перетворень на РДУ ( map
, flatMap
, filter
та інші), код перетворення (закриття) є:
- серіалізовані на вузлі драйвера,
- відправляється до відповідних вузлів кластеру,
- десеріалізований,
- і нарешті виконується на вузлах
Звичайно, ви можете запустити це локально (як у вашому прикладі), але всі ці фази (крім доставки по мережі) все ще відбуваються. [Це дозволяє ловити будь-які помилки ще до розгортання у виробництві]
У вашому другому випадку ви викликаєте метод, визначений у класі, testing
зсередини функції карти. Spark бачить, що і оскільки методи не можуть бути серіалізовані самостійно, Spark намагається серіалізувати весь testing
клас, так що код все одно буде працювати при виконанні в іншому JVM. У вас є дві можливості:
Або ви зробите тестування класу серіалізаційним, щоб весь клас можна було серіалізувати за допомогою 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
}
або ви робите someFunc
функцію замість методу (функції - це об'єкти в Scala), так що 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 {
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
}
Подібна, але не однакова проблема з серіалізацією класів може вас зацікавити, і ви можете прочитати про неї в цій презентації Spark Summit 2013 .
В якості примітки, ви можете переписати rddList.map(someFunc(_))
на rddList.map(someFunc)
, вони точно так же. Зазвичай, друге віддається перевазі, оскільки воно менш дослівне і більш чисте для читання.
EDIT (2015-03-15): SPARK-5307 представив SerializationDebugger, а Spark 1.3.0 - це перша версія, яка його використовує. Це додає шлях серіалізації до NotSerializableException . Коли зустрічається NotSerializableException, налагоджувач відвідує графік об'єкта, щоб знайти шлях до об'єкта, який неможливо серіалізувати, і створює інформацію, щоб допомогти користувачеві знайти об'єкт.
У випадку з ОП це друкується для 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>)