利用scala解析hdfs上tar.gz文件中的xml文件并存入hdfs

前言

 上一篇文章介绍了利用java对tar.gz文件进行解压并对原始xml文件的数据进行解析,如果不了解的可以去看看上一篇文章!!当时就存在很多问题,比如数据存储在hdfs上,上一篇的方法需要将数据解压在本地再上传到hdfs上,数据量过大导致集群IO根本就承受不了!所以就对方案进行整改,目前的架构就是shell脚本从ftp拉取数据后存入hdfs中,如果数据存入hdfs成功就将数据在hdfs上的路径存入kafka,类似spark streaming消费kafka数据(文件的路径)然后对其解压解析,最后再存入hdfs中!本篇文章就介绍其中的一个板块!读取tar.gz压缩文件并解析入库!

依赖

 

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-compress</artifactId>
            <version>1.5</version>
        </dependency>
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>2.11.8</version>
        </dependency>
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-compiler</artifactId>
            <version>2.11.8</version>
        </dependency>
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-reflect</artifactId>
            <version>2.11.8</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-common</artifactId>
            <version>2.7.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
            <version>2.7.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-hdfs</artifactId>
            <version>2.7.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-streaming_2.11</artifactId>
            <version>2.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-streaming-kafka-0-10_2.11</artifactId>
            <version>2.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.kafka</groupId>
            <artifactId>kafka-clients</artifactId>
            <version>1.0.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.kafka</groupId>
            <artifactId>kafka_2.12</artifactId>
            <version>1.0.0</version>
        </dependency>

 

 代码

import java.io.{BufferedInputStream, ByteArrayInputStream, ByteArrayOutputStream}
import java.net.URI
import java.util.zip.GZIPInputStream
import org.apache.commons.compress.archivers.{ArchiveEntry, ArchiveStreamFactory}
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream
import org.apache.hadoop.conf.Configuration
import org.apache.hadoop.fs.{FileSystem, Path}

import scala.xml.XML


object boke {
  def main(args: Array[String]): Unit = {
    val Array(pwd,hdfspath) = args
    var datalist:List[String] = List()
    //path 类似于 /tmp/test/1.tar.gz
    val fileNameList = pwd.split('/')
    if (!pwd.endsWith("tar.gz")) {
      println(pwd + ":不是一个tar.gz文件。")
    }

    val conf = new Configuration();
    val fs = FileSystem.get(URI.create(pwd), conf);
    if(! fs.isFile(new Path(pwd))){
      println(pwd + ":文件不存在。")
    }

    println("start......")
    //读取hdfs上压缩文件   tar.gz文件
    val GZIPInputStream = new GZIPInputStream(fs.open(new Path(pwd)))
    //in 这个流是tar.gz 读取到数据是gz文件
    val in = new ArchiveStreamFactory().createArchiveInputStream("tar", GZIPInputStream)
    var tBufferedInputStream = new BufferedInputStream(in)
    // entry 可以看作是 gz压缩文件
    var entey:ArchiveEntry = null
    while (in.canReadEntryData( entey = in.getNextEntry)){
      //字节缓冲流   存放gz文件的流
      var gzipTmp = new ByteArrayOutputStream
      var b = tBufferedInputStream.read()
      while (b != -1){
        gzipTmp.write(b)
        b = tBufferedInputStream.read()
      }

      if (gzipTmp.size() >0 && gzipTmp.size() == entey.getSize){
        println("gz文件已经读取完成")
      }else{
        println("文件未读取完成")
      }
      //用于存放xml文件的流
      var xmlin = new ByteArrayOutputStream()
      //再次读取gz压缩文件到xml流中
      val is = new GzipCompressorInputStream(new BufferedInputStream(new ByteArrayInputStream(gzipTmp.toByteArray)));
      var c = is.read()
      while (c != -1){
        xmlin.write(c)
        c = is.read()
      }
      if (xmlin.size()>0){
        println("xml文件读取完成")
      }

      //接收输出流 转换输入流 进行xml 解析   xmlData就是xml文件   就可以根据业务逻辑解析
      val xmlData = XML.load(new ByteArrayInputStream(xmlin.toByteArray()))
      //得到fileHeader
      val xmlFileHeader = xmlData \\ "bulkPmMrDataFile" \ "fileHeader"
      val reporttime = (xmlFileHeader \ "@reportTime").toString().replace('T', ' ')
      val starttime = (xmlFileHeader \ "@startTime").toString().replace('T', ' ')
      val endtime = (xmlFileHeader \ "@endTime").toString().replace('T', ' ')
      val s = reporttime+"|"+starttime+"|"+endtime
      datalist = datalist :+ s

      //当数据量过大时,先写入一部分数据
      if (datalist.size >= 3000){
        val conf=new Configuration()
        val sc=FileSystem.get(URI.create(hdfspath),conf)
        val hdfsOutStream = sc.create(new Path(hdfspath+"/"+"1.txt"))
        hdfsOutStream.write(datalist.mkString("|").getBytes("UTF-8"))
        datalist = Nil
        hdfsOutStream.close()
      }
    }

    //循环完之后写入剩余数据
    if (datalist.size >= 0){
      val conf=new Configuration()
      val sc=FileSystem.get(URI.create(hdfspath),conf)
      val hdfsOutStream = sc.create(new Path(hdfspath+"/"+"1.txt"))
      hdfsOutStream.write(datalist.mkString("|").getBytes("UTF-8"))
      datalist = Nil
      hdfsOutStream.close()
    }
  }
}

 以上代码只是我写的一部分示例代码,并不是项目上使用的!只是演示了从hdfs上读取压缩文件并解析入库的一部分内容!在项目的实施过程中,遇到过很多问题,比如如何保证kafka投递的数据不会倾斜(采用的是投递数据前,获取kafka个分区的lag,将数据投递到lag最小的分区),spark streaming读取kafka数据是批处理的,他需要将这一批次处理完成才消费下一批次数据,测试表明,数据量过大根本解析不过来,造成kafka数据堆积(最后解决借用spark streaming的机制,kafka有多少分区就开启多少线程去消费数据,一个线程一直消费一个分区的数据),这个方案都是根据我们的业务逻辑去调整的,如果有什么疑问可以私信我一起探讨学习!!

原创文章!!!!!!

 

posted @ 2021-03-16 17:18  夜空中最亮的仔  阅读(469)  评论(0)    收藏  举报