我注意到使用Spark为项目使用scala 2.10时的奇怪行为,我正在读取属性文件并将所有内容写入Map(loadConfig)中,我还创建了一个返回给定键值的简单方法
问题是,当我在lazy val
类变量中获取所有blackListed名称时,namesBlackList
似乎为空,因为我的所有Person
都拥有“完全访问权限”标记,这是不正确的
然而,当我在<{strong> 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以map
方式执行lazy
方法,所以在lazy val
类中设置Singleton对象的结果变量不会产生正确的结果。
我无法理解为什么它不起作用,更重要的是,除了在方法中调用namesBlackList
之外我无法真正找到解决方法
感谢您的评论。
答案 0 :(得分:3)
有关所需的一些术语和概念的说明,请参阅https://spark.apache.org/docs/latest/programming-guide.html#understanding-closures-a-nameclosureslinka。你的情况会发生什么(我认为):
ConfigManager.loadConfig(path)
在驱动程序节点上运行。 configMap
在那里初始化。
在filterAccess
中,namesBlackList
实际上是一种方法调用。因此,当在工作节点上执行map
内的代码时,此调用就会在那里进行,并在同一节点上访问configMap
,该节点为空。
但是,当你在filterAccess&#34;中写下名字BlackBList然后它是一个局部变量, 成为闭包的一部分,并被序列化。
要解决此问题,您需要为configMap
使用broadcast variable。像
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)
}
答案 1 :(得分:2)
也许您可以尝试在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")
}
}
}