Spark Streaming:如何在foreachRDD函数中更改外部变量的值?

时间:2016-01-27 04:43:49

标签: scala apache-spark spark-streaming

测试代码:

object MaxValue extends Serializable{
    var max = 0
}
object Test {
    def main(args: Array[String]): Unit = {
        val sc = new SparkContext
        val ssc = new StreamingContext(sc, Seconds(5))
        val seq = Seq("testData")
        val rdd = ssc.sparkContext.parallelize(seq)
        val inputDStream = new ConstantInputDStream(ssc, rdd)
        inputDStream.foreachRDD(rdd => { MaxValue.max = 10 })    //I change MaxValue.max value to 10.
        val map = inputDStream.map(a => MaxValue.max)
        map.print    //Why the result is 0? Why not 10?
        ssc.start
        ssc.awaitTermination
    }
}

在这种情况下,如何在 foreachRDD()中更改MaxValue.max的值? map.print的结果为0,为什么不是10.我想在 foreachRDD()中使用 RDD.max() ,所以我需要在 foreachRDD()中更改MaxValue.max值。

你可以帮帮我吗?谢谢!

2 个答案:

答案 0 :(得分:3)

这是不可能的。请记住,RDD方法内部的操作是分布式运行的。因此,MaxValue.max的更改只会在工作人员上执行,而不是在驱动程序上执行。也许如果你说你想要做什么可以帮助导致更好的解决方案,可能使用累加器?

答案 1 :(得分:2)

一般来说,最好避免尝试以这种方式累积值,有不同的方法,如累加器或updateStateByKey可以正确地执行此操作。 为了更好地了解代码中发生的事情,假设您有多个执行程序分配了1个驱动程序和多个分区(最常见的情况)

在驱动程序上运行

inputDStream.foreachRDD(rdd => { MaxValue.max = 10 }) foreachRDD中的代码块在驱动程序上运行,因此它会更新驱动程序上的对象MaxValue

在执行程序上运行

val map = inputDStream.map(a => MaxValue.max)

将分别在每个执行程序上运行lambda,因此将从执行程序的MaxValue获取值(之前从未更新过)。另请注意,每个执行程序都有自己的MaxValue对象版本,因为每个执行程序都位于单独的JVM进程中(通常也位于集群内的单独节点上)。

将代码更改为

val map = inputDStream.map(a => {MaxValue.max=10; MaxValue.max}) 你实际上在执行程序上更新MaxValue,然后在执行程序上获取它 - 所以它的工作原理。 这也应该有效:

val map = inputDStream.map(a => {MaxValue.max=10; a}).map(a => MaxValue.max)

但是,如果您执行以下操作:

val map = inputDStream.map(a => {MaxValue.max= new Random().nextInt(10); a}).map(a => MaxValue.max)

你应该获得具有4个不同整数的记录集(每个分区将具有不同的MaxValue)

意外结果

本地模式

要避免的理由是,根据具体情况,您可以获得更不可预测的结果。例如,如果您运行在群集上返回0 的原始代码,它将以本地模式返回10 ,就像在这种情况下驱动程序一样,并且所有分区都将存在于单个JVM进程中将分享这个对象。所以你甚至可以在这样的代码上创建单元测试,感觉安全但是在部署到集群时 - 开始遇到问题。

作业调度顺序

对于这个我不是100%肯定 - 试图在源代码中找到,但可能会出现另一个问题。在您的代码中,您将有2个工作: 一个是基于你的输出 inputDStream.foreachRDD另一个基于map.print输出。尽管他们最初使用相同的流,但Spark会为他们生成两个独立的DAG,并将安排两个独立的工作,可以完全独立地处理spark,事实上 - 它甚至不必保证工作的执行顺序(它确实保证明显在工作中执行阶段的顺序)如果理论上发生这种情况,它可以在第一个工作之前运行第二个工作,使结果更难以预测