Flink讀寫文件

1 讀取文件-readFile

Q:什麼是文件數據源?
A:Apache Flink提供了一個可重置的數據源連接器,支持將文件中的數據提取成數據流。
(該文件系統是flink的一部分因此無需同Kafka那樣添加依賴包)

示例
val lineReader = new TextInputFormat(null)
streamLocal.readFile[String](
    lineReader,
    //    "hdfs://master:8020/file/a.txt",
    "file:///D:\\tmp\\a.txt",
    FileProcessingMode.PROCESS_CONTINUOUSLY,
    5000L)
    .map(x => {
      val value = x.split(",")
      (value(0), value(1).toInt)
})
  • TextInputFormat:會按(由換行符指定)行來讀取文本文件。
  • PROCESS_CONTINUOUSLY:會以時間間隔週期性的掃描文件,如果目標文件發生了修改,就會將修改過的文件重新讀取一份。(注意這裏的讀取是全部讀取)
  • PROCESS_ONE:不會以時間間隔掃描文件。

2 寫入到文件-StreamingFileSink

  應用配置了檢查點,且讀取的數據源可以提供在故障中恢復的功能,那麼StreamFileSink就可以提供端到端的一次性保障。

  2.1 在瞭解-StreamingFileSink之前你需要了解的知識點

  • 進行,等待,和完成:

    • 當數據寫入文件時,文件會進入“進行” (part-0-0.inprogress)狀態。當RollingPolicy決定生成文件時,原文件會關閉,並通過重命名(part-0-0.wait)進入等待狀態,在下一次檢查點完成後處於等待狀態的文件將(再次重命名part-0-0)進入完成狀態。
  • RollingPolicy:

    • 使用行編碼寫入文件的時候,我們可以通過定義RollingPolicy來決定何時創建分塊文件(什麼是分塊文件會在下面的行編碼中有講到),但在批量編碼中是無法選擇RollingPolicy的。批量編碼只能與檢查點結合起來使用,即當每次生成一個檢查點的時候,就會對應的生成一個新的分塊文件。

    2.1.1 結論

      如果應用沒有開啓檢查點,StreamingFlinkSink將永遠不會把等待狀態的文件變成完成狀態。

  2.2 行編碼

示例
val input = streamLocal.socketTextStream("master", 9999)
val sink = StreamingFileSink.forRowFormat(
    	new Path("file:///D:\\tmp"),
    	new SimpleStringEncoder[String]("UTF-8")
    	).build()
   input.addSink(sink)
  • 自定義BucketAssigner

    • 默認:以程序寫入的時間在tmp下生成每小時一個的目錄
      (該目錄的形成方式是flink通過調用BucketAssigner來完成,我們可以通過修改BucketAssigner的方式來自定義。)
      例如:
      • 數據是2019:08:04 10:10:01秒寫入的,就會在D:\tmp下生成一個2019-08-04 - - 10的一個目錄。
      • 數據是2019:08:04 11:10:01秒寫入的,就會在D:\tmp下生成一個2019-08-04 - - 11的一個目錄。
  • 目錄下的分塊文件

    • 每一個目錄(019-08-04 - -11)中都會包含很多分塊文件(part file),如下,該分塊文件的形成方式是通過StreamingFileSink的多個並行實例來進行併發寫入
      的:下面的分塊文件代表的是:編號爲0的的任務寫出的第0個文件
  • 上面的分塊文件是如何創建的:RollingPolicy?

    • RollingPolicy用來決定任務何時創建一個新的分塊文件。默認是現有文件大小超過128M或打開時間超過60S就會創建一個新的分塊文件。

    2.2.1 行編碼自定義-BucketAssigner

       我們可以通過修改BucketAssigner的方式來自定義

示例
 val sink: StreamingFileSink[String] = StreamingFileSink.forRowFormat(
    new Path("file:///D:\\tmp"),
    new SimpleStringEncoder[String]("UTF-8")
  )
    .withBucketAssigner(new BucketAssigner[String, String] { //對應的是輸入類型和BucketID(目錄名稱)類型
      def getPartitions(element: String): String = {
        val date_L = element.split(",")(1).toLong //將數據中的事件時間作爲目錄進行存儲
        val file_path = new SimpleDateFormat("yyyyMMdd").format(new Date(date_L));
        s"dt=$file_path"
      }

      override def getBucketId(element: String, context: BucketAssigner.Context): String = {
        var partitionValue = ""
        try {
          partitionValue = getPartitions(element)
        } catch {
          case e: Exception => partitionValue = "00000000"
        }
        partitionValue
      }
      override def getSerializer: SimpleVersionedSerializer[String] = {
        SimpleVersionedStringSerializer.INSTANCE;
      }
    }).build()

  2.3 批量編碼

  • 在前面的行編碼中我們使用的寫入方式是行寫入。
  • 在行編碼中每條記錄都會被單獨進行編碼然後添加到分塊文件中,然而在批量編碼模式下,記錄會被攢成批,然後一次性的寫入。Apache Pqrquet會以列式的方式組織和壓縮,因此該文件格式需要以批量編碼的方式寫入。
示例
/**
  * 需要導入的依賴包
  * "org.apache.flink" % "flink-parquet" % "1.7.0"
  * "org.apache.parquet" % "parquet-avro" % "1.10.1"
  * */
object 自定義批量寫入文件  {
  case class Avro_Pojo(name: String, age: Int)
  def main(args: Array[String]): Unit = {
    import org.apache.flink.streaming.api.scala.StreamExecutionEnvironment
    val streamLocal = StreamExecutionEnvironment.createLocalEnvironment(1)
    //由於檢查點爲了支持容錯會以固定間隔來進行創建,在這裏我定義成周期爲10秒
    streamLocal.enableCheckpointing(5000L)
    import org.apache.flink.streaming.api.scala._ //如果數據集是無限的可以引入這個包,來進行隱式轉換操作
    val input = streamLocal.socketTextStream("master", 9999)
      .map(x=>{
      val value = x.split(",")
      val end = Avro_Pojo(value(0), value(1).toInt)
      end
    })

    val sink = StreamingFileSink.forBulkFormat(
      new Path("file:///D:\\tmp"),
      //媽的注意這裏是:forReflectRecord不是forSpecificRecord,小心被坑
      ParquetAvroWriters.forReflectRecord(classOf[Avro_Pojo]))
      .build()
    input.addSink(sink).name("ceshi")

    streamLocal.execute("write file")
  }
}

    2.3.1 批量編碼自定義-BucketAssigner

      批量編碼自定義BucketAssigner,與上面的行編碼自定義是一樣的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章