【Flink】Flink时间概念与水位线

注意:本篇博客中的所有解释都是在滚动窗口的前提下

目录

  • 1 时间概念类型
    • 1.1 事件生成时间(Event Time)
    • 1.2 事件接入时间(Ingestion Time)
    • 1.3 事件处理时间(Processing Time)
    • 1.4 指定时间概念
  • 2 water_mark(水位线)
    • 2.1 water_mark是如何生成的?
    • 2.2 最大允许乱序时间如何理解?
    • 2.3 Flink中的water_marker机制

可互加微信,方便交流(备注请求时注明CSDN来的朋友):a917655983

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中的水位线只会增高不会降低。

你可能感兴趣的:(#,Flink读写外部系统,flink,水位线,watermark,DataStream,最大乱序)