1. 事件时间和水印诞生的背景

    • 在实际的流式计算中数据到来的顺序对计算结果的正确性有至关重要的影响

    • 比如:某数据源中的某些数据由于某种原因(如:网络原因,外部存储自身原因)会有2秒的延时,也就是在实际时间的第1秒产生的数据有可能在第3秒中产生的数据之后到来。

    • 假设在一个5秒的滚动窗口中,有一个EventTime是 9秒的数据,在第11秒时候到来了。

    • 图示:

      • 那么对于一个Count聚合的Tumble(5s)的window,上面的情况如何处理才能window3=3,window2=3 呢?
  2. 时间类型

    • Flink支持不同的时间概念

    • Processing Time(处理时间)

      • 处理时间是指当前机器处理该条事件的时间。
      • 它是当数据流入到具体某个算子时候相应的系统。
      • 他提供了最小的延时和最佳的性能。
      • 但是在分布式和异步环境中, 处理时间不能提供确定性。
      • 因为其对时间到达 系统的速度和数据流在系统的各个operator 之间处理的速度很铭感。
    • Event Time(事件时间)

      • 事件时间是每个事件在其生产设备上发生的时间。
      • 此时间通常在进入Flink之前嵌入到记录中,并且可以从每个记录中提取该事件时间戳。
      • 事件时间对于乱序、延时、或者数据重放等情况,都能给出正确的结果。
      • 事件时间依赖于事件本身,而跟物理时钟没有关系。
      • 基于事件时间的程序必须指定如何生成事件时间水印(watermark),这是指示事件时间进度的机制。
      • 事件时间处理通常存在一定的延时,因此需要为延时和无序的事件等待一段时间。
      • 因此,使用事件时间编程通常需要与处理时间相结合。
    • Ingestion Time(摄入时间)

      • 摄入时间是数据进入Flink框架的时间,是在Source Operator中设置的
      • 与ProcessingTime相比可以提供更可预测的结果,因为摄入时间的时间戳比较稳定(在源处只记录一次)
      • 同一数据在流经不同窗口操作时将使用相同的时间戳
      • 而对于ProcessingTime同一数据在流经不同窗口算子会有不同的处理时间戳
    • Process time 与 Event time对比:

      • 如上图所示,在一个乱序的数据流里,使用event time类型的事件时间,可以保证数据流的顺序性。
    • 设置时间特行

      • Flink程序的第一部分工作通常是设置时间特性,该设置用于定义数据源使用什么时间,在时间窗口处理中使用什么时间。

      • 代码:

        // 设置执行环境, 类似spark中初始化SparkContext
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); env.setParallelism(1); env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime); // env.setStreamTimeCharacteristic(TimeCharacteristic.ProcessingTime); // env.setStreamTimeCharacteristic(TimeCharacteristic.IngestionTime);
  3. Watermark (水印)

    • WaterMark 产生背景

      • 流处理从事件产生,到数据流经source,再到operator,中间是有一个过程和时间的。
      • 虽然大部分情况下,数据流到operator的数据都是按照事件产生的时间顺序来的,但是也不排除由于网络、背压等原因,导致乱序的产生(out-of-order或者说late element)。
      • 但是对于late element(延迟数据),我们又不能无限期的等下去,必须要有个机制来保证一个特定的时间后,必须触发window去进行计算了。
      • 这个特别的机制,就是watermark。
    • WaterMark 介绍

      • Watermark是Flink为了处理EventTime时间类型的窗口计算提出的一种机制, 本质上也是一种时间戳。
      • Watermark是用于处理乱序事件的,而正确的处理乱序事件,通常用watermark机制结合window来实现。
      • 当operator通过基于Event Time的时间窗口来处理数据时,它必须在确定所有属于该时间窗口的消息全部流入此操作符后,才能开始处理数据。
      • 但是由于消息可能是乱序的,所以operator无法直接确认何时所有属于该时间窗口的消息全部流入此操作符。
      • WaterMark包含一个时间戳,Flink使用WaterMark标记所有小于该时间戳的消息都已流入
      • Flink的数据源在确认所有小于某个时间戳的消息都已输出到Flink流处理系统后,会生成一个包含该时间戳的WaterMark,插入到消息流中输出到Flink流处理系统中,Flink operator算子按照时间窗口缓存所有流入的消息。
      • 当操作符处理到WaterMark时,它对所有小于该WaterMark时间戳的时间窗口的数据进行处理并发送到下一个操作符节点,然后也将WaterMark发送到下一个操作符节点。

    • WaterMark 的产生方式

      • Punctuated

        • 数据流中每一个递增的EventTime都会产生一个Watermark。
        • 在实际的生产中Punctuated方式在TPS很高的场景下会产生大量的Watermark在一定程度上对下游算子造成压力,所以只有在实时性要求非常高的场景才会选择Punctuated的方式进行Watermark的生成。
      • Periodic
        • 周期性的(一定时间间隔或者达到一定的记录条数)产生一个Watermark。
        • 在实际的生产中Periodic的方式必须结合时间和积累条数两个维度继续周期性产生Watermark,否则在极端情况下会有很大的延时。
    • 代码:

      package com.ronnie.flink.stream.test;
      
      import org.apache.flink.api.common.functions.MapFunction;
      import org.apache.flink.api.java.tuple.Tuple;
      import org.apache.flink.api.java.tuple.Tuple2;
      import org.apache.flink.streaming.api.TimeCharacteristic;
      import org.apache.flink.streaming.api.datastream.DataStreamSource;
      import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
      import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
      import org.apache.flink.streaming.api.functions.AssignerWithPeriodicWatermarks;
      import org.apache.flink.streaming.api.functions.windowing.WindowFunction;
      import org.apache.flink.streaming.api.watermark.Watermark;
      import org.apache.flink.streaming.api.windowing.time.Time;
      import org.apache.flink.streaming.api.windowing.windows.TimeWindow;
      import org.apache.flink.util.Collector; import javax.annotation.Nullable;
      import java.text.ParseException;
      import java.text.SimpleDateFormat; /**
      *
      hello,2019-09-17 11:34:05.890
      hello,2019-09-17 11:34:07.890
      hello,2019-09-17 11:34:13.890
      hello,2019-09-17 11:34:08.890
      hello,2019-09-17 11:34:16.890
      hello,2019-09-17 11:34:19.890
      hello,2019-09-17 11:34:21.890
      */
      public class WaterMarkTest {
      public static void main(String[] args) {
      StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime); env.setParallelism(1); // 设置多久查看一下当前的水位线... 默认200ms
      env.getConfig().setAutoWatermarkInterval(10000); System.err.println("interval : " + env.getConfig().getAutoWatermarkInterval()); DataStreamSource<String> streamSource = env.socketTextStream("ronnie01", 9999); SingleOutputStreamOperator<String> watermarks = streamSource.assignTimestampsAndWatermarks(new MyWaterMark()); watermarks.map(new MapFunction<String, Tuple2<String, Integer>>() {
      @Override
      public Tuple2<String, Integer> map(String value) throws Exception {
      String[] split = value.split(",");
      String key = split[0]; return new Tuple2<String, Integer>(key, 1);
      }
      }).keyBy(0)
      .timeWindow(Time.seconds(10))
      // 自定义的一个计算规则......
      .apply(new MyWindowFunction())
      .printToErr(); try {
      env.execute();
      } catch (Exception e) {
      e.printStackTrace();
      }
      }
      } class MyWaterMark implements AssignerWithPeriodicWatermarks<String>{ // 目前系统里所有数据的最大事件时间
      long currentMaxTimeStamp = 0;
      // 允许数据延迟5s
      long maxLateTime = 5000; Watermark wm = null; SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); @Nullable
      @Override
      // 周期性地获取目前的水位线时间, 默认200ms
      public Watermark getCurrentWatermark() {
      // 未处理的延迟/乱序问题
      // wm = new Watermark(currentMaxTimeStamp); // 处理数据的延迟/乱序问题
      wm = new Watermark(currentMaxTimeStamp - maxLateTime);
      System.out.println(format.format(System.currentTimeMillis()) + " 获取当前水位线: " + wm + ","+ format.format(wm.getTimestamp()));
      return wm;
      } @Override
      public long extractTimestamp(String element, long previousElementTimestamp) {
      String[] split = element.split(","); String key = split[0]; long timestamp = 0; try {
      //将2019-09-17 10:24:50.958 格式时间转成时间戳
      timestamp = format.parse(split[1]).getTime();
      } catch (ParseException e) {
      e.printStackTrace();
      } // 对比新数据的时间戳和目前最大的时间戳, 取大的值作为新的时间戳
      currentMaxTimeStamp= Math.max(timestamp, currentMaxTimeStamp); System.err.println(key +", 本条数据的时间戳: "+ timestamp + "," +format.format(timestamp)
      + "|目前数据中的最大时间戳: "+ currentMaxTimeStamp + ","+ format.format(currentMaxTimeStamp)
      + "|水位线时间戳: "+ wm + ","+ format.format(wm.getTimestamp())); return timestamp;
      }
      } class MyWindowFunction implements WindowFunction<Tuple2<String, Integer>, String, Tuple, TimeWindow>{ @Override
      public void apply(Tuple tuple, TimeWindow window, Iterable<Tuple2<String, Integer>> input, Collector<String> out) throws Exception {
      SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); int sum = 0; for (Tuple2<String, Integer> tuple2:input){
      sum += tuple2.f1;
      }
      long start = window.getStart();
      long end = window.getEnd(); out.collect("key:" + tuple.getField(0) + " value: " + sum + "| window_start :"
      + format.format(start) + " window_end :" + format.format(end)
      );
      }
      }

事件时间(event time)与水印(watermark)的更多相关文章

  1. 【源码解析】Flink 是如何基于事件时间生成Timestamp和Watermark

    生成Timestamp和Watermark 的三个重载方法介绍可参见上一篇博客: Flink assignAscendingTimestamps 生成水印的三个重载方法 之前想研究下Flink是怎么处 ...

  2. Flink架构(三)- 事件-时间(Event-Time)处理

    3. 事件-时间(Event-Time)处理 在“时间语义”中,我们强调了在流处理应用中时间语义的重要性,并解释了处理时间与事件时间的不同点.处理时间较好理解,因为它基于本地机器的时间,它产生的是有点 ...

  3. 「Flink」事件时间与水印

    我们先来以滚动时间窗口为例,来看一下窗口的几个时间参数与Flink流处理系统时间特性的关系. 获取窗口开始时间Flink源代码 获取窗口的开始时间为以下代码: org.apache.flink.str ...

  4. Flink Streaming基于滚动窗口的事件时间分析

    使用flink-1.9.0进行的测试,在不同的并行度下,Flink对事件时间的处理逻辑不同.包括1.1在并行度为1的本地模式分析和1.2在多并行度的本地模式分析两部分.通过理论结合源码进行验证,得到具 ...

  5. [DOM Event Learning] Section 2 概念梳理 什么是事件 DOM Event

    [DOM Event Learning] Section 2 概念梳理 什么是事件 DOM Event   事件 事件(Event)是用来通知代码,一些有趣的事情发生了. 每一个Event都会被一个E ...

  6. 事件(event),正则

    1.事件(event):事件是可以被 JavaScript 侦测到的行为.网页中的每个元素都可以产生某些可以触发 JavaScript 函数的事件.2.事件源: 触发事件的元素 事件: 被 JavaS ...

  7. TI-RTOS 之 事件同步(Event, 类似semaphore)

    TI-RTOS 之 事件同步(Event, 类似semaphore) Event 是类似Semaphore的存在,官方如下描述: SYS/BIOS events are a means of comm ...

  8. C++多线程同步之事件(Event)

    原文链接:http://blog.csdn.net/olansefengye1/article/details/53291074 一.事件(Event)原理解析 1.线程同步Event,主要用于线程间 ...

  9. 锁机制(Lock) 信号量机制(Semaphore) 事件机制(Event)

    IPC  进程间通信(inter-Process Communicate) 锁机制(Lock) l = Lock() 开启一个锁机制(实例化)   一把锁配一个钥匙 l.acquire()  获得钥匙 ...

随机推荐

  1. 对于在MYSQL_WorkBench中创建新表时对PK NN UQ B UN ZF AI的理解

    1.PK(primary key 主键) 当某项属性勾选了该功能时,该属性会作为与其他对象区别的凭证.例如我们的学号 每个人在本校都是唯一的,但姓名是可能相同的.所以学号就具有主键功能 2.NN(no ...

  2. Android问题:ScrollView默认位置不是最顶部最全解决方案

    描述: Scrollview里面嵌套了一个listview ,这是开发中最寻常的一种布局,遇到的问题是:在这个Scrollview页面默认的起始位置不是最顶部,而是listview的底部. 原因: 在 ...

  3. java中将图片上传到配置好的ftp服务器上

    测试用例: @Test public void testFtp() throws Exception { //1.连接ftp服务器 FTPClient ftpClient = new FTPClien ...

  4. Day3-N - Monthly Expense POJ3273

    Farmer John is an astounding accounting wizard and has realized he might run out of money to run the ...

  5. 「CF741D」Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths

    传送门 Luogu 解题思路 考虑把22个字符状压下来,易知合法情况就是状态中之多有一个1,这个可以暴力一点判断23次. 然后后就是 dsu on the tree 了. 细节注意事项 咕咕咕 参考代 ...

  6. mac java 装机清单

    1. JDK8 2. Eclipse IDE for Enterprise Java Developers 3. maven 4. Postman 5. VS Code 6. finalshell ( ...

  7. Docker + Maven + Docker-compose

    前言: docker:容器化管理 maven:支持docker-maven的插件,通过 mvn clean -Dmaven.test.skip package dockerfile:build 打包命 ...

  8. eclipse环境变量设置

    eclipse的运行需要java,但是当安装了多个版本的jdk后,eclipse可能就不能用了. 解决办法就是: #eclipse 文件夹下有eclipse.ini配置文件,在文件首行添加如下信息: ...

  9. base64和blob

    base64是二进制数据的一个编码格式,就像utf8一样的东西,他跟json一样,也是前后端交互能够相互识别的数据,他更多的是用来传递文件数据,并且如果是图片的base64,可以用来压缩 获取base ...

  10. gitbook简单教程

    简介 GitBook 是一个基于 Node.js 的命令行工具,可使用 Github/Git 和 Markdown 来制作精美的电子书.GitBook支持输出以下几种文档格式 静态站点:GitBook ...