Java ApacheSpark:如何构造Spark应用程序的代码(特别是在使用广播时)
我有一个关于Java Spark应用程序中代码结构的一般性问题。我想将Spark转换的实现代码与调用RDD的代码分开,这样即使在使用包含大量代码行的大量转换时,应用程序的源代码也会保持清晰 我先给你举个简单的例子。在此场景中,flatMap转换的实现作为匿名内部类提供。这是一个简单的应用程序,它读取整数的RDD,然后将每个元素乘以一个整数数组,该数组在以下情况之前广播到所有工作节点:Java ApacheSpark:如何构造Spark应用程序的代码(特别是在使用广播时),java,performance,optimization,apache-spark,Java,Performance,Optimization,Apache Spark,我有一个关于Java Spark应用程序中代码结构的一般性问题。我想将Spark转换的实现代码与调用RDD的代码分开,这样即使在使用包含大量代码行的大量转换时,应用程序的源代码也会保持清晰 我先给你举个简单的例子。在此场景中,flatMap转换的实现作为匿名内部类提供。这是一个简单的应用程序,它读取整数的RDD,然后将每个元素乘以一个整数数组,该数组在以下情况之前广播到所有工作节点: public static void main(String[] args) { SparkConf
public static void main(String[] args) {
SparkConf conf = new SparkConf().setMaster("local").setAppName("MyApp");
JavaSparkContext sc = new JavaSparkContext(conf);
JavaRDD<Integer> result = sc.parallelize(Arrays.asList(5, 8, 9));
final Broadcast<int[]> factors = sc.broadcast(new int[] { 1, 2, 3 });
result = result.flatMap(new FlatMapFunction<Integer, Integer>() {
public Iterable<Integer> call(Integer t) throws Exception {
int[] values = factors.value();
LinkedList<Integer> result = new LinkedList<Integer>();
for (int value : values) result.add(t * value);
return result;
}
});
System.out.println(result.collect()); // [5, 10, 15, 8, 16, 24, 9, 18, 27]
sc.close();
}
这是使用类SparkFunctions
的应用程序的第二个版本:
public static void main(String[] args) {
SparkConf conf = new SparkConf().setMaster("local").setAppName("MyApp");
JavaSparkContext sc = new JavaSparkContext(conf);
JavaRDD<Integer> result = sc.parallelize(Arrays.asList(5, 8, 9));
final Broadcast<int[]> factors = sc.broadcast(new int[] { 1, 2, 3 });
// 1) Initializing
SparkFunctions functions = new SparkFunctions();
// 2) Pass reference of broadcast variable
functions.setFactors(factors);
// 3) Implementation is now in the class SparkFunctions
result = result.flatMap(functions.myFunction);
System.out.println(result.collect()); // [5, 10, 15, 8, 16, 24, 9, 18, 27]
sc.close();
}
publicstaticvoidmain(字符串[]args){
SparkConf conf=new SparkConf().setMaster(“本地”).setAppName(“MyApp”);
JavaSparkContext sc=新的JavaSparkContext(conf);
javarddresult=sc.parallelize(Arrays.asList(5,8,9));
最终广播系数=sc.Broadcast(新的int[]{1,2,3});
//1)初始化
SparkFunctions functions=新的SparkFunctions();
//2)通过广播变量的引用
函数。设定因子(因子);
//3)现在在SparkFunctions类中实现
结果=result.flatMap(functions.myFunction);
System.out.println(result.collect());//[5,10,15,8,16,24,9,18,27]
sc.close();
}
应用程序的两个版本都在工作(本地和集群设置),但我想问它们是否同样有效
问题1:在我看来,Spark序列化了包含广播变量的类SparkFunctions
,并将其发送到工作节点,以便节点可以在其任务中使用该函数。数据是否两次发送到工作节点,第一次是在使用SparkContext
的广播上,第二次是在类SparkFunctions
的序列化上?或者每个元素发送一次(加上1次广播)
问题2:你能为我提供一些关于如何构建源代码的建议吗
请不要提供如何阻止广播的解决方案。我有一个更复杂的实际应用程序
我发现的类似问题实际上没有帮助:
提前感谢您的帮助 这是关于问题1 提交spark作业时,作业分为阶段->任务。这些任务实际上在工作节点上执行转换和操作。驱动程序的sumbitTask()将把有关广播变量的函数和元数据序列化到所有节点 剖析广播工作原理。 驱动程序创建一个本地目录来存储要广播的数据,并启动一个可以访问该目录的HttpServer。调用广播时,数据实际上写入目录(val bdata=sc.broadcast(data))。同时,数据还通过StorageLevel memory+磁盘写入驱动程序的BlockManager。块管理器为数据分配块ID(类型为BroadcastBlockId)
只有当执行器将其接收到的任务反序列化时,才会广播真实数据,它还会以广播对象的形式获取广播变量的元数据。然后调用元数据对象(bdata变量)的readObject()方法。此方法将首先检查本地块管理器,查看是否已经存在本地副本。否则,将从驱动程序中获取数据。提取数据后,数据将存储在本地块管理器中,以供后续使用。我想您已经回答了我的问题。从main方法提供给SparkFunctions类的广播对象只包含元数据,而不包含数据本身。那么这个解决方案应该有效地工作。非常感谢。
public static void main(String[] args) {
SparkConf conf = new SparkConf().setMaster("local").setAppName("MyApp");
JavaSparkContext sc = new JavaSparkContext(conf);
JavaRDD<Integer> result = sc.parallelize(Arrays.asList(5, 8, 9));
final Broadcast<int[]> factors = sc.broadcast(new int[] { 1, 2, 3 });
// 1) Initializing
SparkFunctions functions = new SparkFunctions();
// 2) Pass reference of broadcast variable
functions.setFactors(factors);
// 3) Implementation is now in the class SparkFunctions
result = result.flatMap(functions.myFunction);
System.out.println(result.collect()); // [5, 10, 15, 8, 16, 24, 9, 18, 27]
sc.close();
}