web-dev-qa-db-fra.com

Tâche d'exception non sérialisable lors de l'exécution d'Apache spark job

Le programme Java Java a été écrit pour expérimenter avec Apache spark.

Le programme essaie de lire une liste de mots positifs et négatifs d'un fichier respectif, de le comparer au fichier maître et de filtrer les résultats en conséquence.

import Java.io.Serializable;
import Java.io.FileNotFoundException;
import Java.io.File;
import Java.util.*;
import Java.util.Iterator;
import Java.util.List;
import Java.util.List;
import org.Apache.spark.api.Java.*;
import org.Apache.spark.api.Java.function.Function;

public class SimpleApp implements Serializable{
  public static void main(String[] args) {
    String logFile = "/tmp/master.txt"; // Should be some file on your system
    String positive = "/tmp/positive.txt"; // Should be some file on your system
    String negative = "/tmp/negative.txt"; // Should be some file on your system

    JavaSparkContext sc = new JavaSparkContext("local[4]", "Twitter Analyzer", "/home/welcome/Downloads/spark-1.1.0/", new String[]{"target/scala-2.10/Simple-Assembly-0.1.0.jar"});

    JavaRDD<String> positiveComments = sc.textFile(logFile).cache();

    List<String> positiveList = GetSentiments(positive);
    List<String> negativeList= GetSentiments(negative);

    final Iterator<String> iterator = positiveList.iterator();
    int i = 0;
    while (iterator.hasNext())
    {
      JavaRDD<String> numAs = positiveComments.filter(new Function<String, Boolean>()
      {
        public Boolean call(String s)
        {
          return s.contains(iterator.next());
        }
      });

     numAs.saveAsTextFile("/tmp/output/"+ i);
     i++;
     }

  }

public static List<String> GetSentiments(String fileName) {
  List<String> input = new ArrayList<String>();
try
{
  Scanner sc = new Scanner(new File(fileName));

  while (sc.hasNextLine()) {
      input.add(sc.nextLine());
  }
}
catch (FileNotFoundException e){
    // do stuff here..
}
  return input;
}

}

L'erreur suivante est générée lors de l'exécution de la tâche spark,

 Exception in thread "main" org.Apache.spark.SparkException: Task not serializable
    at org.Apache.spark.util.ClosureCleaner$.ensureSerializable(ClosureCleaner.scala:166)
    at org.Apache.spark.util.ClosureCleaner$.clean(ClosureCleaner.scala:158)
    at org.Apache.spark.SparkContext.clean(SparkContext.scala:1242)
    at org.Apache.spark.rdd.RDD.filter(RDD.scala:282)
    at org.Apache.spark.api.Java.JavaRDD.filter(JavaRDD.scala:78)
    at SimpleApp.main(SimpleApp.Java:37)
    at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:57)
    at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:43)
    at Java.lang.reflect.Method.invoke(Method.Java:606)
    at org.Apache.spark.deploy.SparkSubmit$.launch(SparkSubmit.scala:328)
    at org.Apache.spark.deploy.SparkSubmit$.main(SparkSubmit.scala:75)
    at org.Apache.spark.deploy.SparkSubmit.main(SparkSubmit.scala)
Caused by: Java.io.NotSerializableException: Java.util.ArrayList$Itr
    at Java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.Java:1183)
    at Java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.Java:1547)
    at Java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.Java:1508)
    at Java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.Java:1431)
    at Java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.Java:1177)
    at Java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.Java:1547)
    at Java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.Java:1508)
    at Java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.Java:1431)
    at Java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.Java:1177)
    at Java.io.ObjectOutputStream.writeObject(ObjectOutputStream.Java:347)
    at org.Apache.spark.serializer.JavaSerializationStream.writeObject(JavaSerializer.scala:42)
    at org.Apache.spark.serializer.JavaSerializerInstance.serialize(JavaSerializer.scala:73)
    at org.Apache.spark.util.ClosureCleaner$.ensureSerializable(ClosureCleaner.scala:164)
    ... 12 more

Des pointeurs ??

20
Siva

Lorsque vous créez une classe anonyme, le compilateur fait certaines choses:

JavaRDD<String> numAs = positiveComments.filter(new Function<String, Boolean>()
      {
        public Boolean call(String s)
        {
          return s.contains(iterator.next());
        }
      });

Il sera réécrit comme:

JavaRDD<String> numAs = positiveComments.filter(new Function<String, Boolean>()
      {
        private Iterator<...> $iterator;
        public Boolean call(String s)
        {
          return s.contains($iterator.next());
        }
      });

C'est pourquoi vous pouvez avoir un NotSerializableException car l'itérateur n'est pas sérialisable.

Pour éviter cela, extrayez simplement le résultat du suivant avant:

String value = iterator.next();
JavaRDD<String> numAs = positiveComments.filter(new Function<String, Boolean>()
      {
        public Boolean call(String s)
        {
          return s.contains(value);
        }
      });
14
NoDataFound

Certains Java Faits

  1. Toute classe anonyme définie dans une classe externe fait référence à la classe externe.
  2. Si la classe anonyme doit être sérialisée, elle vous obligera à rendre la classe externe sérialisée.
  3. A l'intérieur de la fonction lambda si l'on utilise une méthode de la classe englobante, la classe doit être sérialisée, si la fonction lambda est sérialisée.

Quelques faits sur Spark.

  1. Sur le même exécuteur, plusieurs tâches peuvent s'exécuter en même temps dans la même machine virtuelle Java, car les tâches sont générées en tant que threads dans spark.
  2. Toute lambda, classe anonyme utilisée avec la fonction de transformation spark (map, mapPartitions, keyBy, redudeByKey…) sera instanciée sur le pilote, sérialisée et envoyée à l'exécuteur.
  3. Sérialiser un objet signifie convertir son état en un flux d'octets afin que le flux d'octets puisse être rétabli en une copie de l'objet.
  4. Un objet Java est sérialisable si sa classe ou l'une de ses super classes implémente soit l'interface Java.io.Serializable, soit sa sous-interface, Java.io.Externalizable, et toutes ses non-transitoires, les champs non statiques sont sérialisables.

Règle générale pour éviter les problèmes de sérialisation:

  1. Évitez d'utiliser une classe anonyme, utilisez plutôt des classes statiques car la classe anonyme vous obligera à sérialiser la classe externe.
  2. Évitez d'utiliser des variables statiques pour contourner le problème de sérialisation, car plusieurs tâches peuvent s'exécuter à l'intérieur de la même machine virtuelle Java et l'instance statique peut ne pas être thread-safe.
  3. Utilisez des variables transitoires pour éviter les problèmes de sérialisation, vous devrez les initialiser dans l'appel de fonction et non dans Constructor. Comme sur driver, le constructeur sera appelé, sur Executor il désérialisera et pour l'objet. la seule façon d'initialiser est à l'intérieur de l'appel de fonction.
  4. Utilisez une classe statique à la place d'une classe anonyme.
  5. Suivez religieusement "attacher des outils sérialisables" uniquement pour les classes qui doivent seulement être sérialisées
  6. À l'intérieur d'une "fonction lambda", ne faites jamais directement référence à la méthode de surclassement, car cela conduira à la sérialisation de la classe externe.
  7. Rendre les méthodes statiques si elles doivent être utilisées directement dans la fonction Lambda, sinon utiliser la notion Class :: func () mais pas func () directement
  8. Java Map <> n'implémente pas Serializable mais HashMap le fait.
  9. Soyez prudent lorsque vous décidez d'utiliser Braodcast vs Raw DataStructures. Si vous voyez un réel avantage, utilisez uniquement Broadcast.

Pour une compréhension approfondie, suivez http://bytepadding.com/big-data/spark/understanding-spark-serialization/

15
KrazyGautam