Apache Spark: How to structure code of a Spark Application (especially when using Broadcasts)
我有一个关于Java Spark应用程序中代码结构的通用问题。我想将实现Spark转换的代码与调用RDD分开,因此即使使用很多包含很多代码行的转换,应用程序的源代码也保持清晰。
我先给你一个简短的例子。在这种情况下,flatMap转换的实现作为匿名内部类提供。这是一个简单的应用程序,它读取整数的RDD,然后将每个元素乘以一个整数数组,该数组在以下之前广播到所有工作程序节点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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(); } |
为了构造代码,我已经将Spark函数的实现提取到另一个类中。类
我已经体验到表示Spark转换的方法可以是静态的,只要它不访问Broadcast变量或Accumulator变量即可。为什么?静态方法只能访问静态属性。对Broadcast变量的静态引用始终为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | @SuppressWarnings("serial") public class SparkFunctions implements Serializable { private Broadcast<int[]> factors; public SparkFunctions() { } public void setFactors(Broadcast<int[]> factors) { this.factors = factors; } public final FlatMapFunction<Integer, Integer> myFunction = 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; } }; } |
这是使用类
的应用程序的第二个版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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(); } |
应用程序的两个版本都可以正常工作(在本地和群集设置中),但是我问它们是否同样有效。
问题1:在我看来,Spark会序列化包含Broadcast变量的类
问题2:您能为我提供一些有关如何以其他方式构造源代码的建议吗?
请不要提供如何防止广播的解决方案。我有一个实际的应用程序,它要复杂得多。
我发现的类似问题并没有真正帮助:
- Spark Java代码结构
- Spark中的BroadCast变量
- Spark:将广播变量传递给执行者
在此先感谢您的帮助!
这与Question1
有关
提交Spark作业时,这些作业被分为阶段->任务。这些任务实际上是在工作程序节点上执行转换和操作的执行。驱动程序的sumbitTask()会将有关广播变量的功能和元数据序列化到所有节点。
广播工作原理的剖析。
驱动程序创建一个本地目录来存储要广播的数据,并启动具有对该目录访问权限的HttpServer。调用广播时,数据实际上已写入目录(val bdata = sc.broadcast(data))。同时,该数据还将通过StorageLevel存储磁盘写入驱动程序的blockManger中。块管理器为数据分配一个blockId(类型BroadcastBlockId)。
仅当执行程序反序列化接收到的任务时,才广播实际数据,并且还以广播对象的形式获取广播变量的元数据。然后,它调用元数据对象(bdata变量)的readObject()方法。此方法将首先检查本plot管理器,以查看是否已存在本地副本。否则,将从驱动程序中获取数据。提取数据后,会将其存储在本plot管理器中以备后用。