我正在尝试使用Spark Streaming处理在JMS队列(QPID)上收到的一些XML数据。将xml作为DStream后,我将它们转换为Dataframes,这样我就可以加入一些已加载的Dataframes形式的静态数据。 但是根据DStream上foreachRdd方法的API文档: 它在Driver上执行,所以这意味着所有处理逻辑只能在Driver上运行而不会分配给worker / executors。
API文档
foreachRDD(func)
应用a的最通用输出运算符 函数,func,从流生成的每个RDD。这个功能 应该将每个RDD中的数据推送到外部系统,例如保存 RDD到文件,或通过网络将其写入数据库。注意 函数func在运行的驱动程序进程中执行 流应用程序,通常会在其中有RDD操作 将强制计算流式RDD。
答案 0 :(得分:12)
所以这意味着所有处理逻辑只能在Driver上运行而不是 分发给工人/执行者。
不,功能本身在驱动程序上运行,但不要忘记它在RDD
上运行。您在RDD
上使用的内部功能,例如foreachPartition
,map
,filter
等仍将在工作节点上运行。这赢了会导致所有数据通过网络发送回驱动程序,除非您调用类似collect
的方法。
答案 1 :(得分:5)
为了清楚说明,如果你运行以下内容,你会看到" monkey"关于司机的标准:
myDStream.foreachRDD { rdd =>
println("monkey")
}
如果你运行以下内容,你会看到" monkey"在驱动程序的标准输出上,过滤器 工作 将在rdd
分发的任何执行程序上完成:
myDStream.foreachRDD { rdd =>
println("monkey")
rdd.filter(element => element == "Save me!")
}
让我们添加myDStream
只接收一个RDD的简化,并且此RDD分布在我们称之为PartitionSetA
的一组分区上MachineSetB
{1}}正在运行ExecutorSetC
。如果您执行以下操作,您将看到" monkey"在司机的标准上,你会看到" turtle"在ExecutorSetC
中的所有执行程序的stdouts(" turtle"每个分区将出现一次 - 许多分区可以在运行执行程序的机器上),以及两个过滤器的工作并且将在ExecutorSetC
:
myDStream.foreachRDD { rdd =>
println("monkey")
rdd.filter(element => element == "Save me!")
rdd.foreachPartition { partition =>
println("turtle")
val x = 1 + 1
}
}
还有一点需要注意的是,在以下代码中,y
最终将通过网络从驱动程序发送到ExecutorSetC
rdd
的所有val y = 2
myDStream.foreachRDD { rdd =>
println("monkey")
rdd.filter(element => element == "Save me!")
rdd.foreachPartition { partition =>
println("turtle")
val x = 1 + 1
val z = x + y
}
}
:
val y = 2
val broadcastY = sc.broadcast(y)
myDStream.foreachRDD { rdd =>
println("monkey")
rdd.filter(element => element == "Save me!")
rdd.foreachPartition { partition =>
println("turtle")
val x = 1 + 1
val z = x + broadcastY.value
}
}
为了避免这种开销,您可以使用广播变量,该变量仅将驱动程序中的值发送到执行程序一次。例如:
{{1}}
为了通过广播变量发送更复杂的内容,例如一旦实例化后不易序列化的对象,您可以看到以下博文:https://allegro.tech/2015/08/spark-kafka-integration.html