Scala 访问数据流集合

Scala 访问数据流集合,scala,apache-spark,apache-kafka,spark-streaming,Scala,Apache Spark,Apache Kafka,Spark Streaming,我试图访问一组过滤后的数据流,如该问题的解决方案所示: 我创建的集合如下所示: val statuCodes = Set("200","500", "404") spanTagStream.cache() val statusCodeStreams = statuCodes.map(key => key -> spanTagStream.filter(x => x._3.get("http.status_code").getOrElse("").asInstan

我试图访问一组过滤后的数据流,如该问题的解决方案所示:

我创建的集合如下所示:

val statuCodes = Set("200","500", "404")
    spanTagStream.cache()
    val statusCodeStreams = statuCodes.map(key => key -> spanTagStream.filter(x => x._3.get("http.status_code").getOrElse("").asInstanceOf[String].equals(key)))
我尝试通过以下方式访问
statusCodeStreams

for(streamTuple <- statusCodeStreams){
      streamTuple._2.foreachRDD(rdd =>
  rdd.foreachPartition(
      partitionOfRecords =>
        {
            val props = new HashMap[String, Object]()
            props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaServers)
            props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,
              "org.apache.kafka.common.serialization.StringSerializer")
            props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,
              "org.apache.kafka.common.serialization.StringSerializer")
            val producer = new KafkaProducer[String,String](props)

            partitionOfRecords.foreach
            {
                 x=>{ 
                 /* Code Writing to Kafka using streamTuple._1 as the topic-String */
                 }
            }
      })
   )
}
for(streamTuple)
foreachPartition(
记录的分区=>
{
val props=new HashMap[String,Object]()
props.put(ProducerConfig.BOOTSTRAP\u服务器\u配置,KafkaServer)
props.put(ProducerConfig.VALUE\u序列化程序\u类\u配置,
“org.apache.kafka.common.serialization.StringSerializer”)
props.put(ProducerConfig.KEY\u序列化程序\u类\u配置,
“org.apache.kafka.common.serialization.StringSerializer”)
val producer=新卡夫卡制作人[字符串,字符串](道具)
记录的分区。foreach
{
x=>{
/*使用streamTuple.\u 1作为主题字符串写入Kafka的代码*/
}
}
})
)
}
执行此操作时,我收到以下错误: java.io.NotSerializableException:
org.apache.spark.streaming.kafka010.DirectKafkaInputDStream的对象正在序列化,可能是RDD操作关闭的一部分。这是因为DStream对象是从闭包中引用的。请在此数据流中重写RDD操作以避免此情况。这是为了避免Spark任务因不必要的对象而膨胀


如何访问流以串行方式写入Kafka?

如异常所示,
DStream
定义被闭包捕获。 一个简单的选项是声明此
DStream
瞬态:

@transient val spamTagStream = //KafkaUtils.create...
@transient
标记要从某个对象的对象图的Java序列化中删除的某些对象。此场景的关键是在闭包中使用与
DStream
在同一范围内声明的一些
val
statusCodeStreams
)。该
val
在闭包中的实际引用是
outer.statusCodeStreams
,导致序列化过程将
outer
的所有上下文“拉”到闭包中。使用
@transient
我们将数据流(以及StreamingContext)声明标记为不可序列化,并避免了序列化问题。根据代码结构(如果在一个
main
函数中都是线性的(糟糕的做法,顺便说一句),可能需要将所有数据流声明+StreamingContext实例标记为
@transient

如果初始筛选的唯一目的是将内容“路由”到单独的卡夫卡主题,那么在
foreachRDD
中移动筛选可能是值得的。这将有助于简化程序结构

spamTagStream.foreachRDD{ rdd => 
    rdd.cache()
    statuCodes.map{code =>
        val matchingCodes = rdd.filter(...)
        matchingCodes.foreachPartition{write to kafka}
    }
    rdd.unpersist(true)
}

使用
@transient
>的优点/缺点是什么?当我进行转换时,是否必须在流的初始阶段,直到我最终到达
spanTagStream
?关于场景:这只是我已经拥有的一些代码片段,可以立即利用收集。其他用例可能会在一定时间内使用流原样或写入RDD,以训练某些机器学习算法,或提供数据点与训练过的模型进行比较。@LST在问题中添加了关于
瞬态
的解释(对于注释来说太长了,我认为它对以后的问题中的参考是有价值的)@transient标记需要在每个数据流值赋值处。通过在单独的类/对象中构造代码并小心闭包的序列化范围,可以避免很多问题。你有解决方法吗(可能是你知道的git项目或类似的项目)通过良好的实践,如何最好地构造应用程序以避免上下文问题,并且不需要标记所有暂时性的内容?我仍处于起步阶段,主要从Spark主页上的教程中学习,并从那里开始。因此,我基本上仍然使用主方法来处理所有问题。@LST我建议您阅读一本书,但这将是赤裸裸的自我介绍-升级:-)一些指导原则让您开始:分别初始化流式处理上下文并将其作为参数传递。将函数与数据流分开:创建一个函数
foreachRDD{…}
,而不是一个巨大的
process(proc:RDD=>Unit)
并将其用作
foreachRDD{process}
。这样做,您的依赖关系变得清晰,b/c您需要调用例如
过程(proc:RDD=>Unit,filter:Set[String])
传递密钥,避免闭包爬行。关于结构化的好建议-我将拆分移动到RDD,并将应用程序结构化为更多的方法调用。现在一切都可以在没有单个
@transient
的情况下运行。我甚至设法过滤RDD,将它们分配到状态集,然后在该集中迭代,以进行最后的过程埃辛。