SparkContext.textFile可以与自定义接收器一起使用吗?

时间:2017-06-26 22:51:13

标签: scala apache-spark spark-streaming

我正在尝试实现一个使用自定义接收器从SQS读取消息的Streaming作业。每条消息都包含对S3文件的单个引用,然后我将其读取,解析并存储为ORC。

这是我到目前为止的代码:

val sc = new SparkContext(conf)
val streamContext = new StreamingContext(sc, Seconds(5))

val sqs = streamContext.receiverStream(new SQSReceiver("events-elb")
  .credentials("accessKey", "secretKey")
  .at(Regions.US_EAST_1)
  .withTimeout(5))

val s3File = sqs.map(messages => {
  val sqsMsg: JsValue = Json.parse(messages)
  val s3Key = "s3://" +
    Json.stringify(sqsMsg("Records")(0)("s3")("bucket")("name")).replace("\"", "") + "/" +
    Json.stringify(sqsMsg("Records")(0)("s3")("object")("key")).replace("\"", "")
  val rawLogs = sc.textFile(s3Key)

  rawLogs
}).saveAsTextFiles("/tmp/output")

不幸的是,这失败并出现以下错误:

Caused by: java.io.NotSerializableException: org.apache.spark.SparkContext
Serialization stack:
    - object not serializable (class: org.apache.spark.SparkContext, value: org.apache.spark.SparkContext@52fc5eb1)
    - field (class: SparrowOrc$$anonfun$1, name: sc$1, type: class org.apache.spark.SparkContext)
    - object (class SparrowOrc$$anonfun$1, <function1>)
at org.apache.spark.serializer.SerializationDebugger$.improveException(SerializationDebugger.scala:40)
at org.apache.spark.serializer.JavaSerializationStream.writeObject(JavaSerializer.scala:46)
at org.apache.spark.serializer.JavaSerializerInstance.serialize(JavaSerializer.scala:100)
at org.apache.spark.util.ClosureCleaner$.ensureSerializable(ClosureCleaner.scala:295)

这是不正确的使用sc.textFile的方法吗?如果是这样,我可以使用什么方法将从SQS收到的每个文件路径转发到文件阅读器进行处理?

FWIW,val s3File最终属于mappedDStream类型。

有关更多背景信息,我将此作为接收方使用:https://github.com/imapi/spark-sqs-receiver

2 个答案:

答案 0 :(得分:1)

实际上,我们不能在sparkContext操作中使用map,因为在阶段转换的闭包在执行程序中运行,其中没有SparkContext定义

解决这个问题的方法是将流程拆分为两个:首先,我们使用现有的map计算文件,但要在textFile操作中使用transform

val s3Keys = sqs.map(messages => {
  val sqsMsg: JsValue = Json.parse(messages)
  val s3Key = "s3://" +
  Json.stringify(sqsMsg("Records")(0)("s3")("bucket")("name")).replace("\"", "") + "/" +
  Json.stringify(sqsMsg("Records")(0)("s3")("object")("key")).replace("\"", "")
}
val files DStream = s3Keys.transform{keys => 
    val fileKeys= keys.collect()
    Val files = fileKeys.map(f=>
      sparkContext.textFile(f))
    sparkContext.union(files)
}
filesDStream.saveAsTextFiles(..)

答案 1 :(得分:0)

没有。这是不对的,因为SparkContext是:

  1. 不可序列化(如您在日志中所见)
  2. 没有意义
  3. 我非常感谢Spark开发者,他们照顾它,所以我们不会忘记它。

    不允许这样使用的原因是SparkContext存在于驱动程序上(或者可以说构成驱动程序)并且负责编排任务(用于Spark作业)。

    执行者很愚蠢,因此只知道如何运行任务。

    Spark不会像这样工作,越早接受设计决策就越能熟练地开发Spark应用程序。

      

    如果是这样,我可以用什么方法将从SQS收到的每个文件路径转发到文件阅读器进行处理?

    我无法回答,因为我从未开发过自定义接收器。