Spark结构化流到kudu上下文

时间:2017-10-26 07:28:18

标签: apache-spark-sql spark-streaming apache-kudu

我想阅读kafka主题,然后通过spark streaming将其写入kudu表。

我的第一个方法

// sessions and contexts
val conf = new SparkConf().setMaster("local[2]").setAppName("TestMain")
val sparkSession = SparkSession.builder().config(conf).getOrCreate()
val sparkContext = sparkSession.sparkContext
val kuduContext = new KuduContext("...", sparkContext);

// structure
val schema: StructType = StructType(
  StructField("userNo", IntegerType, true) ::
  StructField("bandNo", IntegerType, false) ::
  StructField("ipv4", StringType, false) :: Nil);

// kudu - prepare table
kuduContext.deleteTable("test_table");
kuduContext.createTable("test_table", schema, Seq("userNo"), new CreateTableOptions()
  .setNumReplicas(1)
  .addHashPartitions(List("userNo").asJava, 3))

// get stream from kafka
val parsed = sparkSession
  .readStream
  .format("kafka")
  .option("kafka.bootstrap.servers", "...")
  .option("startingOffsets", "latest")
  .option("subscribe", "feed_api_band_get_popular_post_list")
  .load()
  .select(from_json(col("value").cast("string"), schema).alias("parsed_value"))

// write it to kudu
kuduContext.insertRows(parsed.toDF(), "test_table");

现在抱怨

Exception in thread "main" org.apache.spark.sql.AnalysisException: Queries with streaming sources must be executed with writeStream.start();;
kafka
at org.apache.spark.sql.catalyst.analysis.UnsupportedOperationChecker$.org$apache$spark$sql$catalyst$analysis$UnsupportedOperationChecker$$throwError(UnsupportedOperationChecker.scala:297)
at org.apache.spark.sql.catalyst.analysis.UnsupportedOperationChecker$$anonfun$checkForBatch$1.apply(UnsupportedOperationChecker.scala:36)

我的第二种方法

似乎我改变了我的代码以使用传统的KafkaUtils.createDirectStream

KafkaUtils.createDirectStream[String, String](
  ssc,
  PreferConsistent,
  Subscribe[String, String](topics, kafkaParams)
).foreachRDD(rdd => {
  rdd.foreach(record => {
    // write to kudu.............
    println(record.value());
  })
});

ssc.start();
ssc.awaitTermination();

那么,哪一个是正确的方法?或者有没有办法让它从第一种方法运行?

Spark版本是2.2.0。

4 个答案:

答案 0 :(得分:1)

两种方法似乎都是正确的。第一个使用Spark结构化流式处理方式,其中数据以表格形式附加。第二种方法是通过传统的DStream做事方式来实现的。

答案 1 :(得分:1)

我相信,目前尚无Kudu支持将KuduContext与Spark结构化流一起使用。我遇到了类似的问题,不得不依靠传统的Kudu Client并实现了ForeachWriter [Row]类。我使用了示例here,并能够实现解决方案。

答案 2 :(得分:0)

第一种方法是错误的,正如您已经可以从错误中看到的那样,它非常清楚:Queries with streaming sources must be executed with writeStream.start()。那只会批量处理。

第二个使用DStream,因此不使用结构化流。

有第三种方法和第四种方法。

从Kudu 1.9.0开始,此issue固定版本支持结构化流,并按预期使用:

    parsed
      .writeStream
      .format("kudu")
      .option("kudu.master", kuduMaster)
      .option("kudu.table", tableName)
      .option("kudu.operation", operation)
      .start()

请注意,如果您使用的是Cloudera,则此方法仅适用于cdh6.2.0及更高版本:

<!-- https://mvnrepository.com/artifact/org.apache.kudu/kudu-spark2 -->
<dependency>
    <groupId>org.apache.kudu</groupId>
    <artifactId>kudu-spark2_2.11</artifactId>
    <version>1.9.0-cdh6.2.0</version>
    <scope>test</scope>
</dependency>

我的解决方案是查看code from SparkContext,看看kuduContext.insertRows(df, table)和其他方法的作用,然后创建一个ForeachWriter[Row]

val kuduContext = new KuduContext(master, sparkContext)

  parsed
    .toDF()
    .writeStream
    .foreach(new ForeachWriter[Row] {
      override def open(partitionId: Long, version: Long): Boolean =
        kuduContext.tableExists(table)

      override def process(value: Row): Unit = {
        val kuduClient = kuduContext.syncClient
        val kuduSession = kuduClient.newSession()
        kuduSession.setFlushMode(SessionConfiguration.FlushMode.AUTO_FLUSH_BACKGROUND)
        kuduSession.setIgnoreAllDuplicateRows(ignoreDuplicates)

        val kuduTable = kuduClient.openTable(kuduSinkConfiguration.table)
        val operation = getOperationFunction(kuduTable) //get the kuduTable.newInsert(), newUpsert(), etc.
        kuduSession.setIgnoreAllDuplicateRows(ignoreDuplicates)

        val row = operation.getRow
        row.add("userNo", value.getAs[Int]("userNo"))
        row.add("bandNo", value.getAs[Int]("bandNo"))
        row.add("ipv4", value.getAs[String]("ipv4"))
        kuduSession.apply(operation)

        kuduSession.flush()
        kuduSession.close()
      }

      override def close(errorOrNull: Throwable): Unit = Unit

    })
    .start()

答案 3 :(得分:0)

我们还可以使用Spark版本2.2.0和cloudera版本CDH 5.14将结构化流数据加载到Kudu表中。您只需要下载与CDH6.2对应的spark-kudu-2.2.11 jar并将其作为jar传递到您的spark-submit命令中即可。这将在下面的语句中标识kudu格式,并轻松加载数据框。

已解析 .writeStream .format(“ kudu”) .option(“ kudu.master”,kuduMaster) .option(“ kudu.table”,tableName) .option(“ kudu.operation”,操作) .start()

JAR可以从以下网址下载:https://mvnrepository.com/artifact/org.apache.kudu/kudu-spark2_2.11/1.10.0-cdh6.3.2

火花提交声明:

spark2-submit --master local [*] --deploy-mode client --jars spark-sql-kafka-0-10_2.11-2.2.0.jar,kafka-clients-0.10.0.0.jar, spark-streaming-kafka-0-10_2.11-2.2.0.jar,kudu-spark2_2.11-1.10.0-cdh6.3.2.jar,kudu-client-1.10.0-cdh6.3.2.jar / path_of_python_code / rdd-stream-read.py

注意-Kudu-client是可选的。可能必须与群集部署模式一起使用。

使用的writestream语句:

query = dfCols.writeStream.format(“ kudu”)。option(“ kudu.master”,“ host:7051,host:7051,host:7051”)。option(“ kudu.table”,“黑斑羚: :db.kudu_table_name“).option(” kudu.operation“,” upsert“).option(” checkpointLocation“,” file:/// path_of_dir / checkpoint /“).start()