如何在流数据集中加载tar.gz文件?

时间:2017-12-30 12:46:39

标签: apache-spark spark-structured-streaming

我想从tar-gzip文件(tgz)进行流式传输,其中包括我的实际CSV存储数据。

当我的数据以CSV文件形式出现时,我已经设法使用spark 2.2进行结构化流式处理,但实际上,数据是以gzip压缩的csv文件形式出现的。

结构化流式传输完成的触发器在处理CSV流之前是否有解压缩的方法?

我用来处理文件的代码是:

val schema = Encoders.product[RawData].schema
val trackerData = spark
  .readStream
  .option("delimiter", "\t")
  .schema(schema)
  .csv(path)
val exceptions = rawCientData
  .as[String]
  .flatMap(extractExceptions)
  .as[ExceptionData]
当路径指向csv文件时,

按预期生成输出。 但我想使用tar gzip文件。 当我尝试将这些文件放在给定路径时,我没有任何异常,批输出告诉我

  "sources" : [ {
    "description" : "FileStreamSource[file:/Users/matthias/spark/simple_spark/src/main/resources/zsessionlog*]",
    "startOffset" : null,
    "endOffset" : {
      "logOffset" : 0
    },
    "numInputRows" : 1095,
    "processedRowsPerSecond" : 211.0233185584891
  } ],

但我没有处理任何实际数据。 控制台接收器如下所示:

+------+---+-----+
|window|id |count|
+------+---+-----+
+------+---+-----+

2 个答案:

答案 0 :(得分:1)

认为在Spark中可以阅读tar.gz文件(有关一些想法,请参阅Read whole text files from a compression in Sparkgzip support in Spark)。

Spark确实支持gzip文件,但建议不要将它们拆分为单个分区(这反过来会使Spark几乎没有帮助)。

为了在Spark Structured Streaming中加载gzip压缩文件,您必须指定路径模式,以便文件包含在加载中,比如说zsessionlog*.csv.gz或类似。否则,仅csv仅加载CSV文件。

如果您坚持使用Spark Structured Streaming来处理tar.gz文件,您可以编写自定义流数据Source来执行取消 - tar.gz

鉴于不建议将gzip文件作为Spark中的数据格式,使用Spark Structured Streaming的整个想法没有多大意义。

答案 1 :(得分:0)

我解决了以这种方式阅读.tar.gz(.tgz)文件的部分: 受此site的启发,我创建了自己的TGZ编解码器

final class DecompressTgzCodec extends CompressionCodec {
  override def getDefaultExtension: String = ".tgz"

  override def createOutputStream(out: OutputStream): CompressionOutputStream = ???
  override def createOutputStream(out: OutputStream, compressor: Compressor): CompressionOutputStream = ???
  override def createCompressor(): Compressor = ???
  override def getCompressorType: Class[_ <: Compressor] = ???

  override def createInputStream(in: InputStream): CompressionInputStream = {
    new TarDecompressorStream(new TarArchiveInputStream(new GzipCompressorInputStream(in)))
  }
  override def createInputStream(in: InputStream, decompressor: Decompressor): CompressionInputStream = createInputStream(in)

  override def createDecompressor(): Decompressor = null
  override def getDecompressorType: Class[_ <: Decompressor] = null

  final class TarDecompressorStream(in: TarArchiveInputStream) extends DecompressorStream(in) {
    def updateStream(): Unit = {
      // still have data in stream -> done
      if (in.available() <= 0) {
        // create stream content from following tar elements one by one
        in.getNextTarEntry()
      }
    }

    override def read: Int = {
      checkStream()
      updateStream()
      in.read()
    }

    override def read(b: Array[Byte], off: Int, len: Int): Int = {
      checkStream()
      updateStream()
      in.read(b, off, len)
    }

    override def resetState(): Unit = {}
  }
}

并将其注册以供spark使用。

val conf = new SparkConf()
conf.set("spark.hadoop.io.compression.codecs", classOf[DecompressTgzCodec].getName)

val spark = SparkSession
  .builder()
  .master("local[*]")
  .config(conf)
  .appName("Streaming Example")
  .getOrCreate()

完全像我希望的那样工作。