将数据从Spark-Streaming存储到Cassandra时出现问题

时间:2016-09-07 07:20:17

标签: apache-spark serialization cassandra spark-streaming

SparkStreaming上下文从RabbitMQ读取流,间隔为30秒。我想修改cassandra中存在的相应行的几列值,然后想要将数据存储回Cassandra。为此,我需要检查特定主键的行是否存在于Cassandra中,如果,是,则获取它并执行必要的操作。但问题是,我在驱动程序上创建StreamingContext并在Worker上执行操作。因此,他们无法获得StreamingContext对象的原因是因为它没有被序列化并发送给工作人员而且我收到此错误: java.io.NotSerializableException: org.apache.spark.streaming.StreamingContext。我也知道我们无法访问foreachRDD中的StreamingContext。但是,如何在不产生序列化错误的情况下实现相同的功能?

我看过几个例子here,但它没有帮助。

以下是代码的摘要:

   val ssc = new StreamingContext(sparkConf,30)
    val receiverStream = RabbitMQUtils.createStream(ssc, rabbitParams)
    receiverStream.start()      
    val lines = receiverStream.map(EventData.fromString(_))
    lines.foreachRDD{ x => if (x.toLocalIterator.nonEmpty) {
                x.foreachPartition { it => for (tuple <- it) { 
                val cookieid  = tuple.cookieid                
                val sessionid = tuple.sessionid              
                val logdate = tuple.logdate
                val EventRows =  ssc.cassandraTable("SparkTest", CassandraTable).select("*")
                .where("cookieid = '" + cookieid + "' and logdate = '" + logdate+ "' and sessionid = '" + sessionid + "')

                   Somelogic Whether row exist or not for Cookieid

                }  } }

2 个答案:

答案 0 :(得分:1)

SparkContext无法序列化并传递到可能不同节点中的多个worker。如果你需要做这样的事情你可以使用forEachPartiion,mapPartitons。 否则,使用传递的函数来执行此操作

 CassandraConnector(SparkWriter.conf).withSessionDo { session =>
  ....
    session.executeAsync(<CQL Statement>)

并且在SparkConf中你需要提供Cassandra细节

  val conf = new SparkConf()
    .setAppName("test")
    .set("spark.ui.enabled", "true")
    .set("spark.executor.memory", "8g")
    //  .set("spark.executor.core", "4")
    .set("spark.eventLog.enabled", "true")
    .set("spark.eventLog.dir", "/ephemeral/spark-events")
    //to avoid disk space issues - default is /tmp
    .set("spark.local.dir", "/ephemeral/spark-scratch")
    .set("spark.cleaner.ttl", "10000")
    .set("spark.cassandra.connection.host", cassandraip)
    .setMaster("spark://10.255.49.238:7077")

Java CSCParser是一个不可序列化的库。因此,如果在RDD上调用map或forEach,Spark无法向其发送可能不同的节点。一种解决方法是使用mapPartion,在这种情况下,一个完整的Parition将在一个SparkNode中执行。因此,无需为每个调用序列化。例如

val rdd_inital_parse = rdd.mapPartitions(pLines).

 def pLines(lines: Iterator[String]) = {
    val parser = new CSVParser() ---> Cannot be serialized, will fail if using rdd.map(pLines)
    lines.map(x => parseCSVLine(x, parser.parseLine))
  }

答案 1 :(得分:0)

尝试使用x.sparkContext.cassandraTable()而不是ssc.cassandraTable()并查看是否有帮助