繁体   English   中英

Scala类的惰性val变量与Spark的奇怪行为

[英]Scala class lazy val variables strange behaviour with Spark

我在使用Scala 2.10的项目中使用Spark时发现一个奇怪的行为,正在读取属性文件并将所有内容写入Map(loadConfig)内,并且我还创建了一个简单的方法来返回给定键的值。

问题是,当我在lazy val类变量中获得所有blackListed名称时, namesBlackList似乎为空,因为我的所有Person都具有“完全访问权”标签,这是不正确的

然而,当我写namesBlackList filterAccess然后一切工作得很好。

ConfigManager.scala

object ConfigManager extends Serializable {

  private var configMap = Map.empty[String, String]

  def loadConfig(configPath:String) = {
    // Reads a key/value properties file and writes it in the configmap
  }

  def getParameter(parameter: String): String = configMap.getOrElse(parameter, s"${parameter}=>UNKNOWN")
}

AnalyseData.scala

object AnalyseData extends Serializable {

    private lazy val namesBlackList = ConfigManager.getParameter("names.blacklist").toSet

    def filterAccess(rdd:RDD[Person]) : RDD[Person] = {
        rdd.map {person => 
          if (namesBlackList.contains(person.firstName))
            (person.firstName,person.lastName,"limited access")
          else
            (person.firstName,person.lastName,"full Access")  
       }
    }
}

AnalyseService.scala

object AnalyseService extends Serializable {
    def main(path:String) {
        ConfigManager.loadConfig(path)

        val datas = createNameRdd // reads from a db and create a RDD[Person]

        val filteredData = AnalyseData.filterAccess(datas)

    }
}

我试图调整代码中的所有内容,但由于Spark以lazy方式执行map方法,因此在lazy val类变量中设置Singleton对象的结果将不会产生正确的结果。 我不明白为什么它不起作用,更重要的是,除了在方法内部调用namesBlackList之外,我真的找不到解决方法。

感谢您的意见。

有关所需的某些术语和概念的说明,请参见https://spark.apache.org/docs/latest/programming-guide.html#understanding-closures-a-nameclosureslinka 您的情况会怎样(我认为):

  1. ConfigManager.loadConfig(path)在驱动程序节点上运行。 configMap在那里初始化。

  2. filterAccessnamesBlackList是一个方法调用。 因此,当在工作程序节点上执行map内的代码时,此调用在此处发生,并访问同一节点(空)上的configMap

  3. 但是,当您“在filterAccess内写入namesBlackList”时,它是一个局部变量,并且确实成为闭包的一部分并被序列化。

要解决此问题,您需要为configMap使用广播变量 就像是

object ConfigManager extends Serializable {

  private var configMap: Broadcast[Map[String, String]] = _

  def loadConfig(configPath:String) = {
    // Reads a key/value properties file and writes it in the configmap
  }

  def getParameter(parameter: String): String = configMap.value.getOrElse(parameter, s"${parameter}=>UNKNOWN")
}

最好避免var

def main(path:String) {
    val configMap = ConfigManager.loadConfig(path)

    val datas = createNameRdd(configMap) // reads from a db and create a RDD[Person]

    val filteredData = AnalyseData.filterAccess(datas, configMap)
}

也许您可以尝试在filterAccess方法内部(但在闭包外部)强制执行lazy val ,如下filterAccess

object AnalyseData extends Serializable {

  private lazy val namesBlackList = ConfigManager.getParameter("names.blacklist").toSet

  def filterAccess(rdd:RDD[Person]) : RDD[Person] = {
      val localNamesBlackList = namesBlackList       //force the lazy val...
      rdd.map {person => 
        if (localNamesBlackList.contains(person.firstName))
          (person.firstName,person.lastName,"limited access")
        else
          (person.firstName,person.lastName,"full Access")  
     }
  }
}

暂无
暂无

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

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