web-dev-qa-db-fra.com

Spark Lanceur en attente de fin de travail à l'infini

J'essaie de soumettre un JAR avec un travail Spark dans le cluster YARN à partir de Java. J'utilise SparkLauncher pour soumettre un exemple SparkPi:

Process spark = new SparkLauncher()
    .setAppResource("C:\\spark-1.4.1-bin-hadoop2.6\\lib\\spark-examples-1.4.1-hadoop2.6.0.jar")
    .setMainClass("org.Apache.spark.examples.SparkPi")
    .setMaster("yarn-cluster")
    .launch();
System.out.println("Waiting for finish...");
int exitCode = spark.waitFor();
System.out.println("Finished! Exit code:" + exitCode);

Il y a deux problèmes:

  1. Lors de la soumission en mode "groupe de fils", l'application est soumise avec succès à YARN et s'exécute avec succès (elle est visible dans l'interface utilisateur YARN, signalée comme SUCCÈS et pi est imprimé dans la sortie). Cependant, la demande de soumission n'est jamais notifiée que le traitement est terminé - il se bloque indéfiniment après l'impression "En attente de terminer ..." Le journal du conteneur peut être trouvé ici
  2. Lors de la soumission en mode "client-fil", l'application n'apparaît pas dans l'interface utilisateur YARN et l'application soumise se bloque à "En attente de terminer ..." Lorsque le code suspendu est tué, l'application apparaît dans l'interface utilisateur YARN et il est signalé comme SUCCÈS, mais la sortie est vide (pi n'est pas imprimé). Le journal du conteneur peut être trouvé ici

J'ai essayé d'exécuter l'application d'envoi avec Oracle Java 7 et 8.

16
TomaszGuzialek

J'ai obtenu de l'aide dans la liste de diffusion Spark. La clé est de lire/effacer getInputStream et getErrorStream () sur le processus. Le processus enfant peut remplir le tampon et provoquer un blocage - voir - Documents Oracle concernant le processus . Les flux doivent être lus dans des threads séparés:

Process spark = new SparkLauncher()
    .setSparkHome("C:\\spark-1.4.1-bin-hadoop2.6")
    .setAppResource("C:\\spark-1.4.1-bin-hadoop2.6\\lib\\spark-examples-1.4.1-hadoop2.6.0.jar")
    .setMainClass("org.Apache.spark.examples.SparkPi").setMaster("yarn-cluster").launch();

InputStreamReaderRunnable inputStreamReaderRunnable = new InputStreamReaderRunnable(spark.getInputStream(), "input");
Thread inputThread = new Thread(inputStreamReaderRunnable, "LogStreamReader input");
inputThread.start();

InputStreamReaderRunnable errorStreamReaderRunnable = new InputStreamReaderRunnable(spark.getErrorStream(), "error");
Thread errorThread = new Thread(errorStreamReaderRunnable, "LogStreamReader error");
errorThread.start();

System.out.println("Waiting for finish...");
int exitCode = spark.waitFor();
System.out.println("Finished! Exit code:" + exitCode);

où la classe InputStreamReaderRunnable est:

public class InputStreamReaderRunnable implements Runnable {

    private BufferedReader reader;

    private String name;

    public InputStreamReaderRunnable(InputStream is, String name) {
        this.reader = new BufferedReader(new InputStreamReader(is));
        this.name = name;
    }

    public void run() {
        System.out.println("InputStream " + name + ":");
        try {
            String line = reader.readLine();
            while (line != null) {
                System.out.println(line);
                line = reader.readLine();
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
17
TomaszGuzialek

Comme il s'agit d'un ancien message, je voudrais ajouter une mise à jour qui pourrait aider qui lira ce message après. Dans spark 1.6.0 il y a quelques fonctions ajoutées dans la classe SparkLauncher. Ce qui est:

def startApplication(listeners: <repeated...>[Listener]): SparkAppHandle

http://spark.Apache.org/docs/latest/api/scala/index.html#org.Apache.spark.launcher.SparkLauncher

Vous pouvez exécuter l'application sans avoir besoin de threads supplémentaires pour la peluche de gestion stdout et stderr. Il y a un bon rapport d'état de l'application en cours d'exécution. Utilisez ce code:

  val env = Map(
      "HADOOP_CONF_DIR" -> hadoopConfDir,
      "YARN_CONF_DIR" -> yarnConfDir
    )
  val handler = new SparkLauncher(env.asJava)
      .setSparkHome(sparkHome)
      .setAppResource("Jar/location/.jar")
      .setMainClass("path.to.the.main.class")
      .setMaster("yarn-client")
      .setConf("spark.app.id", "AppID if you have one")
      .setConf("spark.driver.memory", "8g")
      .setConf("spark.akka.frameSize", "200")
      .setConf("spark.executor.memory", "2g")
      .setConf("spark.executor.instances", "32")
      .setConf("spark.executor.cores", "32")
      .setConf("spark.default.parallelism", "100")
      .setConf("spark.driver.allowMultipleContexts","true")
      .setVerbose(true)
      .startApplication()
println(handle.getAppId)
println(handle.getState)

Vous pouvez continuer à demander l'état si l'application spark jusqu'à ce qu'elle réussisse. Pour plus d'informations sur le fonctionnement du serveur de lancement Spark dans 1.6.0. Voir ce lien) : https://github.com/Apache/spark/blob/v1.6.0/launcher/src/main/Java/org/Apache/spark/launcher/LauncherServer.Java

8
Abdulrahman

J'ai implémenté à l'aide de CountDownLatch, et cela fonctionne comme prévu. C'est pour SparkLauncher version 2.0.1 et cela fonctionne également en mode cluster de fils.

    ...
final CountDownLatch countDownLatch = new CountDownLatch(1);
SparkAppListener sparkAppListener = new SparkAppListener(countDownLatch);
SparkAppHandle appHandle = sparkLauncher.startApplication(sparkAppListener);
Thread sparkAppListenerThread = new Thread(sparkAppListener);
sparkAppListenerThread.start();
long timeout = 120;
countDownLatch.await(timeout, TimeUnit.SECONDS);    
    ...

private static class SparkAppListener implements SparkAppHandle.Listener, Runnable {
    private static final Log log = LogFactory.getLog(SparkAppListener.class);
    private final CountDownLatch countDownLatch;
    public SparkAppListener(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }
    @Override
    public void stateChanged(SparkAppHandle handle) {
        String sparkAppId = handle.getAppId();
        State appState = handle.getState();
        if (sparkAppId != null) {
            log.info("Spark job with app id: " + sparkAppId + ",\t State changed to: " + appState + " - "
                    + SPARK_STATE_MSG.get(appState));
        } else {
            log.info("Spark job's state changed to: " + appState + " - " + SPARK_STATE_MSG.get(appState));
        }
        if (appState != null && appState.isFinal()) {
            countDownLatch.countDown();
        }
    }
    @Override
    public void infoChanged(SparkAppHandle handle) {}
    @Override
    public void run() {}
}
4
Elkhan Dadashov