从docker容器运行SparkApp时,对在另一个容器中运行的Spark运行时出错

时间:2016-10-11 19:28:22

标签: java scala apache-spark docker dependencies

我有一个困扰我几天的问题,而且我的想法很不错。

我构建了一个Spark docker容器,Spark在独立模式下运行。主人和工人都在那里开始。这是在Azure中运行的计算机。

现在我尝试将Spark Scala应用程序部署在一个单独的容器(同一台机器)中,在那里我传递Spark主URL和我需要连接到Spark的其他东西。连接是无缝的。

我遇到的第一个问题是:

Exception in thread "main" org.apache.spark.SparkException: Job aborted due to stage failure: Task 0 in stage 0.0 failed 4 times, most recent failure: Lost task 0.3 in stage 0.0 (TID 3, 10.1.0.4): java.lang.ClassNotFoundException: org.apache.spark.streaming.kafka.KafkaRDDPartition

然后我创建了一个除Spark之外的依赖项文件夹,将它们放在我的应用程序JAR文件旁边的文件夹中,并使用SparkConf.setJars将它们添加到SparkConf,

现在奇怪的事情发生了:

Exception in thread "main" org.apache.spark.SparkException: Job aborted due to stage failure: Task 0 in stage 0.0 failed 4 times, most recent failure: Lost task 0.3 in stage 0.0 (TID 3, 10.1.0.4): java.lang.ClassCastException: cannot assign instance of scala.collection.immutable.List$SerializationProxy to field org.apache.spark.rdd.RDD.org$apache$spark$rdd$RDD$$dependencies_ of type scala.collection.Seq in instance of org.apache.spark.rdd.MapPartitionsRDD

除此之外,如果我只使用java -cp <dependencies(including spark jars) cp> myApp.jar从本地计算机运行scala应用程序,它运行正常,作业运行正常。

我本地没有SPARK_HOME,而setJars基本上是一个空列表,好像我不会使用它,它仍然有效。 我想它在运行我的应用程序时使用类路径中提供的jar,我不需要提供任何其他内容。

如果你们中的任何人有任何想法我会很感激,我真的无法解释自己为什么这不起作用我直到现在才做任何Spark部署。我主要使用嵌入式Spark。

Spark在我的应用程序依赖项(2.0.0)中与在docker容器中运行的版本相同。 我用过:
Scala 2.11.7 for my app
两个容器上的Java 1.8(app,spark)

这里要求的是我的应用程序的代码

  val jars = Option(new File(Properties.spark_jars_path).listFiles()).toList.flatten.map(_.getAbsolutePath)
  val conf = new SparkConf()
    .setMaster(RunUtils.determineMasterUrl(Properties.mode))
    .setAppName(RunUtils.SPARK_APP_NAME)
    .setJars(jars)
    .set("spark.cassandra.connection.host", Properties.cassandra_connection_host)

  val ssc = new StreamingContext(conf, Seconds(1))

  case class Result(buyDate: Timestamp, endDate: Timestamp, maxDate: Timestamp, buyAmount: Double, buyRate: Double)

  def main(args: Array[String]): Unit = {

    val DateFormatter = new java.text.SimpleDateFormat("yyyy-MM-dd")

    val kafkaParams = Map("metadata.broker.list" -> Properties.kafka_brokers, "auto.offset.reset" -> Properties.kafka_auto_reset_offset)

    //
    // BITSTAMP
    //
    val bitstampTopic = Set("bitstamp_trades")
    val bitstampStream = KafkaUtils.createDirectStream[String, String, StringDecoder, StringDecoder](ssc, kafkaParams, bitstampTopic)
    val bitstampTradeStream = bitstampStream.map(_._2).map { trade =>
      val jsonNode = JsonMapper.readTree(trade)
      Trade(
        "BITSTAMP",
        "BTC_USD",
        if(jsonNode.get("type").asInt() == 1) "SELL" else "BUY",
        DateFormatter.format(new Date(jsonNode.get("timestamp").asLong() * 1000)),
        new Date(jsonNode.get("timestamp").asLong() * 1000),
        jsonNode.get("amount").asDouble(),
        jsonNode.get("price").asDouble()
      )
    }

    bitstampTradeStream.saveToCassandra("coin_master", "trades", SomeColumns(
      "exchange_house",
      "exchange_currencies",
      "exchange_type",
      "date",
      "trade_time",
      "amount",
      "price")
    )
   ssc.start()
   ssc.awaitTermination()
  }

1 个答案:

答案 0 :(得分:0)

好的,问题是我的地图功能。

更具体地说,这一行是问题所在:

val jsonNode = JsonMapper.readTree(trade)

JsonMapper实际上是一个来自Jackson库的已配置的ObjectMapper,为了使它工作,我应该使用sparkContext.broadcast,因为必须在每个执行器上调用一些方法。

您可以在此处详细了解原因: Spark: broadcasting jackson ObjectMapper

因此,在将我的代码更改为类似的内容之后,它起作用了:

val broadcastValue = ssc.sparkContext.broadcast(JsonMapper)

val kafkaParams = Map("metadata.broker.list" -> Properties.kafka_brokers, "auto.offset.reset" -> Properties.kafka_auto_reset_offset)

//
// BITSTAMP
//
val bitstampTopic = Set("bitstamp_trades")
val bitstampStream = KafkaUtils.createDirectStream[String, String, StringDecoder, StringDecoder](ssc, kafkaParams, bitstampTopic)
val bitstampTradeStream = bitstampStream.map(_._2).map { trade =>
  broadcastValue.value.registerModule(new DefaultScalaModule with RequiredPropertiesSchemaModule)
  val jsonNode = broadcastValue.value.readTree(trade)
  Trade(
    "BITSTAMP",
    "BTC_USD",
    if(jsonNode.get("type").asInt() == 1) "SELL" else "BUY",
    DateFormatter.format(new Date(jsonNode.get("timestamp").asLong() * 1000)),
    new Date(jsonNode.get("timestamp").asLong() * 1000),
    jsonNode.get("amount").asDouble(),
    jsonNode.get("price").asDouble()
  )
}