我在转换函数中访问变量时遇到问题。有人可以帮帮我吗? 这是我的相关课程和功能。
@SerialVersionUID(889949215L)
object MyCache extends Serializable {
@transient lazy val logger = Logger(getClass.getName)
@volatile var cache: Broadcast[Map[UUID, Definition]] = null
def getInstance(sparkContext: SparkContext) : Broadcast[Map[UUID, Definition]] = {
if (cache == null) {
synchronized {
val map = sparkContext.cassandraTable("keyspace", "table")
.collect()
.map(m => m.getUUID("id") ->
Definition(m.getString("c1"), m.getString("c2"), m.getString("c3"),
m.getString("c4"))).toMap
cache = sparkContext.broadcast(map)
}
}
cache
}
}
在另一个档案中:
object Processor extends Serializable {
@transient lazy val logger = Logger(getClass.getName)
def processData[T: ClassTag](rawStream: DStream[(String, String)], ssc: StreamingContext,
processor: (String, Broadcast[Map[UUID, Definition]]) => T): DStream[T] = {
MYCache.getInstance(ssc.sparkContext)
var newCacheValues = Map[UUID, Definition]()
rawStream.cache()
rawStream
.transform(rdd => {
val array = rdd.collect()
array.foreach(r => {
val value = getNewCacheValue(r._2, rdd.context)
if (value.isDefined) {
newCacheValues = newCacheValues + value.get
}
})
rdd
})
if (newCacheValues.nonEmpty) {
logger.info(s"Rebroadcasting. There are ${newCacheValues.size} new values")
logger.info("Destroying old cache")
MyCache.cache.destroy()
// this is probably wrong here, destroying object, but then referencing it. But I haven't gotten to this part yet.
MyCache.cache = ssc.sparkContext.broadcast(MyCache.cache.value ++ newCacheValues)
}
rawStream
.map(r => {
println("######################")
println(MyCache.cache.value)
r
})
.map(r => processor(r._2, MyCache.cache.value))
.filter(r => null != r)
}
}
每次运行时,我都会在尝试访问cache.value
时获得SparkException: Failed to get broadcast_1_piece0 of broadcast_1
当我在println(MyCache.cache.values)
我能够访问广播变量之后立即添加.getInstance
时,但当我将其部署到mesos群集时,我无法访问再次广播值,但是有一个空指针异常。
更新
我看到的错误在println(MyCache.cache.value)
上。我不应该添加这个包含destroy的if语句,因为我的测试从未达到过。
我的应用程序的基础知识是,我在cassandra中有一张表格,它不会被更新。但我需要对某些流数据进行一些验证。所以我想将这个表中的所有数据(不是更新)提取到内存中。 getInstance
在启动时拉出整个表格,然后检查我的所有流数据,看看我是否需要再次从cassandra中取出(我将很少这样做)。转换和收集是我检查是否需要提取新数据的地方。但由于我的表有可能会更新,我需要偶尔更新广播。所以我的想法是摧毁它然后重播。一旦我得到其他工作,我会更新。
如果我注释掉destroy并重播,我会得到同样的错误。
另一次更新:
我需要在processor
这一行中访问广播变量:.map(r => processor(r._2, MyCache.cache.value))
。
我能够在变换中广播变量,如果我在变换中做println(MyCache.cache.value)
,那么我的所有测试都会通过,然后我就可以在{{{{}}中访问广播1}}
更新
processor
这是我到达此行时得到的堆栈跟踪。
rawStream
.map(r => {
println("$$$$$$$$$$$$$$$$$$$")
println(metrics.value)
r
})
答案 0 :(得分:2)
[更新回答]
您收到错误是因为rawStream.map
内的代码(MyCache.cache.value
)正在其中一个执行程序上执行,而MyCache.cache仍然是null
!
当您执行MyCache.getInstance
时,它会在驱动程序上创建MyCache.cache
值,并将其广播好。但是,您并未在map
方法中引用相同的对象,因此它不会被发送给执行程序。相反,由于您直接引用MyCache
,执行程序会在自己的MyCache.cache
对象副本上调用MyCache
,这显然是空的。
首先在驱动程序中获取cache
广播对象的实例并在地图中使用那个对象,您可以按预期工作。以下代码应该适合您 -
val cache = MYCache.getInstance(ssc.sparkContext)
rawStream.map(r => {
println(cache.value)
r
})