小白学习Flink系列--第三篇(概念)之Watermark

watermark

什么是watermark?

watermark中文译为水位线,听到这里大家可能就懵逼了,水位线是什么东西?莫慌,听我一一道来

watermark出现原因

在上一节我们说过,processing Time使用的是本地时间节点,所以每一次取到的都是递增有序的,而对于Event Time呢,是事件创建的时间,时间和记录是绑定的。那么问题就来了,由于网络等原因,数据可能会存在乱序,比如前一条数据被堵住,而后一条数据发送成功。如下图所示:

理想的情况下是这样的:

img

而由于网络等原因,部分数据存在延迟了,变成这样:

img

显然是存在乱序了,那么该如何处理呢?

Flink设计了一个很聪明的方式,其核心意思就是保证批次有序,这里的批次并不是指多少条为一个批次,而是指另一个概念–窗口,我们稍后会讲到。看一下Flink是如何做到的?

第一步,先将数据划分为一些批次,注意:是以窗口划分,如下图所示,划分为了三部分,装逼术语就叫做离散化

在这里插入图片描述

第二步,在批次间插入标志位,可以仔细观察一下标志位数据,均为批次内最大,他表示的就是以后到来的数据再也没有小于这个时间了。所以Watermark的本质是时间戳

在这里插入图片描述

如何使用

Talk is cheap. Show me the code,原理哔哔完了,你倒是告诉我代码怎么写呀

  • Watermark的两种生成方式

    • SourceFunction中产生,将Timestamp的分配(也就是上文提到的离散化)和watermark的生成放在上游,同时sourceFunction中也有两个方法生成watermark

      • 通过collectwithTimestamp方法发送数据,和调用emitWatermark产生watermark,我们可以看到,调用collectwithTimestamp需要传入两个参数,第一个参数就是数据,第二次参数就是数据对应的时间戳,这样就完成了timestamp的分配,调用emitWatermark生成watermark

        override def run(ctx: SourceContext[MyType]): Unit = {
            while (/* condition */) {
                val next: MyType = getNext()
                ctx.collectWithTimestamp(next, next.eventTimestamp)
        
                if (next.hasWatermarkTime) {
                    ctx.emitWatermark(new Watermark(next.getWatermarkTime))
                }
            }
        }
        
    • DataStream API指定,调用assignTimestampsAndWatermarks方法,用于某些sourceFunction不支持的情况,它能够接收不同的timestamp和watermark生成器,说白了就是函数里面参数不同

      • 定期生成

        
        val resultData = logData.assignTimestampsAndWatermarks(new AssignerWithPeriodicWatermarks[(Long, String, Long)] {
          
          val maxOutOfOrderness = 10000L
        
          var currentMaxTimestamp: Long = _ 
        
          override def getCurrentWatermark: Watermark = {
            new Watermark(currentMaxTimestamp - maxOutOfOrderness)
          }
          
          // 根据数据本身的 Event time 来获取
          override def extractTimestamp(element: (Long, String, Long), previousElementTimestamp: Long): Long = {
            val timestamp = element._1
            currentMaxTimestamp = Math.max(timestamp, currentMaxTimestamp)
            timestamp
          }
        })
        
      • 标记生成

        class PunctuatedAssigner extends AssignerWithPunctuatedWatermarks[SensorReading] {
         
          // 1 min in ms
          val bound: Long = 60 * 1000
         
          override def checkAndGetNextWatermark(r: SensorReading, extractedTS: Long): Watermark = {
            if (r.id == "sensor_1") {
              // emit watermark if reading is from sensor_1
              new Watermark(extractedTS - bound)
            } else {
              // do not emit a watermark
              null
            }
          }
         
          override def extractTimestamp(r: SensorReading, previousTS: Long): Long = {
            // assign record timestamp
            r.timestamp
          }
        }
        
      • 区别:定期指的是定时调用逻辑生成watermark,而标记不是根据时间,而是看到特殊记录表示接下来的数据可能发不过来了,分配timestamp 调用用户实现的watermark方法

      • 建议:越靠近源端处理更容易进行判断

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值