在不停止进程

时间:2017-07-24 13:16:52

标签: apache-spark amazon-s3 spark-streaming spark-dataframe snappydata

在我的应用程序中,我从Kafka队列获得了一个帐户流(使用带有kafka的Spark流)

我需要从S3获取与这些帐户相关的属性,因此我计划缓存S3结果数据帧,因为S3数据暂时不会更新至少一天,未来可能会很快变为1小时或10分钟。所以问题是我如何定期刷新缓存的数据帧而不停止进程。

**更新:我计划在S3中发生更新时将事件发布到kafka,使用SNS和AWS lambda,我的流应用程序将订阅该事件并根据此事件刷新缓存的数据帧(基本上是unpersist( )缓存并从S3重新加载)  这是一个好方法吗?

2 个答案:

答案 0 :(得分:5)

最近在Spark Mailing List

上提出了这个问题

据我所知,执行您所要求的唯一方法是在新数据到达时从S3重新加载DataFrame,这意味着您还必须重新创建流式DF并重新启动查询。这是因为DataFrames基本上是不可变的。

如果要在不重新加载DataFrame的情况下更新(mutate)数据,则需要尝试其中一个datastores that integrate with or connect to Spark并允许突变。我所知道的是SnappyData

答案 1 :(得分:0)

实现的最简单方法,下面的代码读取每个批次的维度数据文件夹,但请记住,新的维度数据值(在我的情况下为国家/地区名称)必须是一个新文件。

package com.databroccoli.streaming.dimensionupateinstreaming

import org.apache.log4j.{Level, Logger}
import org.apache.spark.sql.{DataFrame, ForeachWriter, Row, SparkSession}
import org.apache.spark.sql.functions.{broadcast, expr}
import org.apache.spark.sql.types.{StringType, StructField, StructType, TimestampType}

object RefreshDimensionInStreaming {

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

    @transient lazy val logger: Logger = Logger.getLogger(getClass.getName)

    Logger.getLogger("akka").setLevel(Level.WARN)
    Logger.getLogger("org").setLevel(Level.ERROR)
    Logger.getLogger("com.amazonaws").setLevel(Level.ERROR)
    Logger.getLogger("com.amazon.ws").setLevel(Level.ERROR)
    Logger.getLogger("io.netty").setLevel(Level.ERROR)

    val spark = SparkSession
      .builder()
      .master("local")
      .getOrCreate()

    val schemaUntyped1 = StructType(
      Array(
        StructField("id", StringType),
        StructField("customrid", StringType),
        StructField("customername", StringType),
        StructField("countrycode", StringType),
        StructField("timestamp_column_fin_1", TimestampType)
      ))

    val schemaUntyped2 = StructType(
      Array(
        StructField("id", StringType),
        StructField("countrycode", StringType),
        StructField("countryname", StringType),
        StructField("timestamp_column_fin_2", TimestampType)
      ))

    val factDf1 = spark.readStream
      .schema(schemaUntyped1)
      .option("header", "true")
      .csv("src/main/resources/broadcasttest/fact")

    var countryDf: Option[DataFrame] = None: Option[DataFrame]

    def updateDimensionDf() = {
      val dimDf2 = spark.read
        .schema(schemaUntyped2)
        .option("header", "true")
        .csv("src/main/resources/broadcasttest/dimension")

      if (countryDf != None) {
        countryDf.get.unpersist()
      }

      countryDf = Some(
        dimDf2
          .withColumnRenamed("id", "id_2")
          .withColumnRenamed("countrycode", "countrycode_2"))

      countryDf.get.show()
    }

    factDf1.writeStream
      .outputMode("append")
      .foreachBatch { (batchDF: DataFrame, batchId: Long) =>
        batchDF.show(10)

        updateDimensionDf()

        batchDF
          .join(
            countryDf.get,
            expr(
              """
      countrycode_2 = countrycode 
      """
            ),
            "leftOuter"
          )
          .show

      }
      .start()
      .awaitTermination()

  }

}