如何在Spark上运行不同工作人员的任务?

时间:2017-09-04 06:40:45

标签: java apache-spark

我有以下Spark代码:

package my.spark;

import java.util.ArrayList;
import java.util.List;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.sql.SparkSession;

public class ExecutionTest {    
    public static void main(String[] args) {
        SparkSession spark = SparkSession
                .builder()
                .appName("ExecutionTest")
                .getOrCreate();

        JavaSparkContext jsc = new JavaSparkContext(spark.sparkContext());

        int slices = 2;
        int n = slices;
        List<String> list = new ArrayList<>(n);
        for (int i = 0; i < n; i++) {
            list.add("" + i);
        }

        JavaRDD<String> dataSet = jsc.parallelize(list, slices);

        dataSet.foreach(str -> {
            System.out.println("value: " + str);
            Thread.sleep(10000);
        });

        System.out.println("done");

        spark.stop();
    }

}

我使用以下命令运行主节点和两个worker(localhost; Windows上的所有内容):

bin\spark-class org.apache.spark.deploy.master.Master

和(两次):

bin\spark-class org.apache.spark.deploy.worker.Worker spark://<local-ip>:7077

一切都正常开始。

使用命令提交作业后:

bin\spark-submit --class my.spark.ExecutionTest --master spark://<local-ip>:7077 file:///<pathToFatJar>/FatJar.jar

命令已启动,但value: 0value: 1输出由其中一个工作人员编写(显示在与工作人员关联的页面上的Logs > stdout)。第二名工人在Logs > stdout中没有任何内容。据我所知,这意味着每次迭代都由同一个工人完成。

如何在两个不同的正在运行的工作程序上运行这些任务?

1 个答案:

答案 0 :(得分:0)

这是可能的,但我不确定它是否能够随时随地正常工作。但是,在测试时,每次都按预期工作。

我使用Windows 10 x64的主机和4台虚拟机(VM)测试了我的代码:VirtualBox with Debian 9(stretch)内核4.9.0 x64,Host-Only网络,Java 1.8.0_144,Apache Spark 2.2 .0 for Hadoop 2.7( spark-2.2.0-bin-hadoop2.7.ta​​r.gz )。

我一直在VM上使用master和3个slave,在Windows上使用了一个slave:

  • debian-master - 1个CPU,1 GB RAM
  • debian-slave1 - 1个CPU,1 GB RAM
  • debian-slave2 - 1个CPU,1 GB RAM
  • debian-slave3 - 2个CPU,1 GB RAM
  • windows-slave - 4个CPU,8 GB RAM

我将我的作业从Windows机器提交到位于VM上的主机。

开头与之前相同:

    SparkSession spark = SparkSession
            .builder()
            .config("spark.cores.max", coresCount) // not necessary
            .appName("ExecutionTest")
            .getOrCreate();

[重要] coresCount对于分区至关重要 - 我必须使用已使用核心的数量对数据进行分区,工作人员/执行者数量。< / p>

接下来,我必须创建JavaSparkContext和RDD。重用RDD允许多次执行同一组工作。

    JavaSparkContext jsc = new JavaSparkContext(spark.sparkContext());

    JavaRDD<Integer> rddList
          = jsc.parallelize(
                    IntStream.range(0, coresCount * 2)
                             .boxed().collect(Collectors.toList()))
               .repartition(coresCount);

我创建了rddListcoresCount * 2个元素。等于coresCount的元素数量不允许在所有关联的工作者上运行(在我的情况下)。也许,coresCount + 1就足够了,但我没有对其进行测试,因为coresCount * 2不太好。

接下来要做的是运行命令:

    List<String> hostsList
        = rddList.map(value -> {
                Thread.sleep(3_000);
                return InetAddress.getLocalHost().getHostAddress();
            })
            .distinct()
            .collect();

    System.out.println("-----> hostsList = " + hostsList);

Thread.sleep(3_000)是正确分配任务所必需的。 3秒对我来说已经足够了。可能价值可能更小,有时可能需要更高的价值(我猜这个价值取决于工人从主人那里获得执行任务的速度)。

上面的代码将在与worker关联的每个核心上运行,因此每个worker不止一个。要在每个worker上运行一个命令,我使用了以下代码:

/* as static field of class */
private static final AtomicBoolean ONE_ON_WORKER = new AtomicBoolean(false);

...

    long nodeCount
        = rddList.map(value -> {
                Thread.sleep(3_000);
                if (ONE_ON_WORKER.getAndSet(true) == false) {
                    System.out.println("Executed on "
                            + InetAddress.getLocalHost().getHostName());
                    return 1;
                } else {
                    return 0;
                }
            })
            .filter(val -> val != 0)
            .count();

    System.out.println("-----> finished using #nodes = " + nodeCount);

当然,最后,停止:

    spark.stop();