web-dev-qa-db-ja.com

Sparkランチャーはジョブの完了を無限に待っています

SparkジョブをYARNクラスターにJavaコードから送信)してJARを送信しようとしています。SparkLauncherを使用して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);

2つの問題があります。

  1. 「yarn-cluster」モードで送信すると、アプリケーションは正常にYARNに送信され、正常に実行されます(YARN UIに表示され、SUCCESSとして報告され、piが出力に出力されます)。ただし、サブミットするアプリケーションは処理が終了したことを通知されません-「Waiting to finish ...」と印刷した後、無限にハングします。コンテナのログが見つかります here
  2. 「yarn-client」モードで送信すると、アプリケーションはYARN UIに表示されず、送信アプリケーションは「Waiting to finish ...」でハングします。ハングしているコードが強制終了されると、アプリケーションはYARN UIに表示され、次のように報告されます。成功ですが、出力は空です(piは出力されません)。コンテナのログが見つかります ここ

Oracle Java 7および8の両方で送信アプリケーションを実行しようとしました。

16
TomaszGuzialek

Sparkメーリングリストで助けを得ました。重要なのは、プロセスのgetInputStreamおよびgetErrorStream()を読み取る/クリアすることです。子プロセスがバッファをいっぱいにしてデッドロックを引き起こす可能性があります-参照- プロセスに関するOracleのドキュメント 。ストリームは別のスレッドで読み取る必要があります。

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);

ここで、InputStreamReaderRunnableクラスは次のとおりです。

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

これは古い投稿なので、この投稿を誰が読んだかを知るのに役立つアップデートを追加したいと思います。 spark 1.6.0では、SparkLauncherクラスにいくつかの追加された関数があります。

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

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

Stdoutおよびstderr処理のプラッシュに追加のスレッドを必要とせずにアプリケーションを実行できます。実行中のアプリケーションのニースステータスレポートがあります。このコードを使用してください:

  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)

sparkアプリケーションが成功するまで状態をエンキューし続けることができます。Spark Launcherサーバーが1.6.0でどのように機能するかについては、このリンクを参照してください。 : https://github.com/Apache/spark/blob/v1.6.0/launcher/src/main/Java/org/Apache/spark/launcher/LauncherServer.Java

8
Abdulrahman

CountDownLatchを使用して実装しましたが、期待どおりに動作します。これはSparkLauncherバージョン2.0.1向けで、Yarn-clusterモードでも動作します。

    ...
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