Flink時間概念與水位線

注意:本篇博客中的所有解釋都是在滾動窗口的前提下

1 時間概念類型

1.1 事件生成時間(Event Time)

    在事件時間模式下,Flink流式應用處理的所有記錄都必須要包含時間戳。
    這個時間戳是記錄所對應事件的發生時間,但實際上我們也可以自定義時間戳,但只要保證流記錄的時間戳會隨着數據流的前進大致遞增即可。

1.2 事件接入時間(Ingestion Time)

    Ingestion Time,是數據通過第三方進入到Flink,Flink接收數據的時間,因此如果按照事件的接入時間,來處理數據,是不能處理亂序情況下的數據(如果數據是亂序到達)。

1.3 事件處理時間(Processing Time)

    即數據接入Flink後,通過算子處理數據的時間,使用的是當前主機的時間。

1.4 指定時間概念

    Flink流式處理中,絕大部分的業務都會使用eventTime,一般只在eventTime無法使用時,纔會考慮其他的時間屬性。
    Flink默認採用的時Process Time時間概念

示例 a:指定時間概念

object addSink到kafka {
  def main(args: Array[String]): Unit = {
    import org.apache.flink.streaming.api.scala.StreamExecutionEnvironment
    val streamLocal = StreamExecutionEnvironment.createLocalEnvironment(3)
    //這裏配值我們在處理數據的時候,使用EventTime事件時間
    streamLocal.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
    streamLocal.setStreamTimeCharacteristic(TimeCharacteristic.IngestionTime)
    streamLocal.setStreamTimeCharacteristic(TimeCharacteristic.ProcessingTime)

    import org.apache.flink.api.scala._ //如果數據是有限的(靜態數據集)可以引入這個包
    val dataStream = streamLocal.fromElements(("flink_er", 3), ("f", 1), ("c", 2), ("c", 1), ("d", 5))
      .map(x => x._1)
    //flink-connector-kafka的版本要保持一致,都是1.7.0

    val kafkaProducer010 = new FlinkKafkaProducer[String]("master:9092", "ceshi01", new SimpleStringSchema())
    dataStream.addSink(kafkaProducer010)

    streamLocal.execute()
  }
}

2 water_mark(水位線)

    通常來講,由於各種原因,包含但不限於網絡、外部系統因素等,事件數據往往不能夠及時傳輸到Flink系統中進行計算,因此,在開啓EventTime的前提下,flink提供了一種依據watermark(水位線)機制結合window(窗口)來實現對亂序數據的處理的方式。

2.1 water_mark是如何生成的?

生成water_mark的方式主要是有兩大類:

  • with Periodic water_mark
  • with Punctuated water_mark

第一種可以定義一個最大允許亂序的時間,這種情況應用較多。

示例 b:對DataStream中的時間生成水位線
(在這裏我添加了一些額外的if和print代碼片段,目的是爲了在2.3中對我提出的假設允以驗證)
  val watermark: DataStream[(String, Long, String, Int)] = inputMap.assignTimestampsAndWatermarks(new AssignerWithPeriodicWatermarks[(String, Long, String, Int)] {
    val maxOutOfOrderness = 10000L //最大允許的亂序時間是10s
    var currentMaxTimestamp = 0L
    var a: Watermark = _

    override def getCurrentWatermark: Watermark = {
      a = new Watermark(currentMaxTimestamp - maxOutOfOrderness)
      a
    }


    override def extractTimestamp(element: (String, Long, String, Int), previousElementTimestamp: Long): Long = {
      val timestamp = element._2
      currentMaxTimestamp = Math.max(timestamp, currentMaxTimestamp)
      val end = if (!a.toString.contains("-")) {
        val regEx = "[^0-9]";
        val p = Pattern.compile(regEx);
        val m = p.matcher(a.toString);
        val L_number = m.replaceAll("").trim()
        format.format(L_number.toLong)
      } else a.toString
      println("timestamp:" + element._1 + "," + element._2 + "," + element._3 + "|" +
        s"${a.toString}($end)" + "|" + format.format(currentMaxTimestamp - maxOutOfOrderness))
      val lll: Long = System.currentTimeMillis()

      timestamp
    }
  })

2.2 最大允許亂序時間如何理解?

    實際上可以理解爲數據集中的元素沒有按照順序到達,而是其中某個元素延遲到達了,那麼此時整個數據集,就是亂序的;因此纔會有這個“允許最大亂序時間”的概念,即允許事件數據延遲多久到達。
(數據有可能會延遲到達,但我們又不能無限期的等待下去,必須有個機制來保證一個特定的事件後,必須觸發windows去進行當前窗口的數據,這個特別的機制就是water_mark)

2.3 Flink中的water_marker機制

  •    每接收一條數據就相當於往水池中添加水,因此水位線的高度只會升高不會降低,每當一個新的數據進來時,會重新計算水位線時間。每條數據中的water_marker記錄的是截至到現在最高的水位線,每條數據計算水位線的時候如果小於當前水位線時間,則不會更新現有的水位線(如下方的timestamp:4)

  •    當水位線到達窗口觸發時間時纔會觸發窗口的計算,water_marker的意義在於數據無序傳遞的時候可以讓其保持一定的容錯率,如果晚來的數據在這個容錯率之內,會當作正常傳遞來的數據進行處理。

如下樣例中的輸出數據

輸出數據爲:
當前數據 | 當前數據的水位線 | 計算並更新水位線
timestamp:1,1586947680000,18:48:00 | Watermark@-10000(Watermark@-10000) | 18:47:50
timestamp:2,1586947740000,18:49:00 | Watermark@1586947670000(18:47:50) | 18:48:50
timestamp:3,1586947800000,18:50:00 | Watermark@1586947730000(18:48:50) | 18:49:50
timestamp:4,1586947620000,18:47:00 | Watermark@1586947790000(18:49:50) | 18:49:50
timestamp:5,1586947680000,18:48:00|Watermark@1586947790000(18:49:50) | 18:49:50
timestamp:6,1586947260000,18:41:00 | Watermark@1586947790000(18:49:50) | 18:49:50
timestamp:7,1586947980000,18:53:00 | Watermark@1586947790000(18:49:50) | 18:52:50
timestamp:8,1586947920000,18:52:00 | Watermark@1586947970000(18:52:50) | 18:52:50

以上輸出數據的形成過程:
   輸入數據:
   1,1586947680000
   2,1586947740000
    … …
   water_mark生成方式
   本篇博文中的示例 b代碼所示;

結論:
   可以發現我的輸入數據是亂序達到的,有的到達的時間早有的到達的時間晚,但就算是如此,我的水位線依然不受其影響,因此得證,Flink中的水位線只會增高不會降低。

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