繁体   English   中英

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

[英]Spark Streaming: How to change the value of external variables in foreachRDD function?

测试代码:

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值。

你可以帮帮我吗? 谢谢!

这是不可能的。 请记住,RDD方法内部的操作是分布式运行的。 因此,对MaxValue.max的更改将仅在工作程序上执行,而不在驱动程序上执行。 也许您说的是尝试使用累加器可以帮助您找到更好的解决方案?

通常,最好避免尝试以这种方式累加值,可以通过诸如累加器或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单独处理,实际上-它甚至不必保证作业的执行顺序(它确实保证明显在一个工作中执行阶段的顺序),如果理论上发生这种情况,它可以在第一个工作之前执行第二个工作,从而使结果更难以预测

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM