[英]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 。 您的情况会怎样(我认为):
ConfigManager.loadConfig(path)
在驱动程序节点上运行。 configMap
在那里初始化。
在filterAccess
, namesBlackList
是一个方法调用。 因此,当在工作程序节点上执行map
内的代码时,此调用在此处发生,并访问同一节点(空)上的configMap
。
但是,当您“在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.