无法在转化

时间:2016-06-24 00:05:07

标签: scala apache-spark broadcast

我在转换函数中访问变量时遇到问题。有人可以帮帮我吗? 这是我的相关课程和功能。

@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
    })

1 个答案:

答案 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
             })