Flink如何设置初始水印

时间:2018-02-02 05:43:55

标签: apache-flink flink-streaming

我正在使用带有scala的Flink 1.3.2构建流媒体应用程序,我的Flink应用程序将监视文件夹并将新文件流式传输到管道中。文件中的每条记录都有一个时间戳关联。我想使用此时间戳作为事件时间并使用AssignerWithPeriodicWatermarks[T]构建水印,我的水印生成器如下所示:

class TimeLagWatermarkGenerator extends AssignerWithPeriodicWatermarks[Activity] {
    val maxTimeLag = 6 * 3600000L // 6 hours
    override def extractTimestamp(element: Activity, previousElementTimestamp: Long): Long = {
    val format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX")
        val timestampString = element.getTimestamp
    }
    override def getCurrentWatermark(): Watermark = {
      new Watermark(System.currentTimeMillis() - maxTimeLag)
    }
  }

env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
env.getConfig.setAutoWatermarkInterval(10000L)
val stream = env.readFile(inputformart, path, FileProcessingMode.PROCESS_CONTINUOUSLY, 100)

val activity = stream
      .assignTimestampsAndWatermarks(new TimeLagWatermarkGenerator())
      .map { line =>
        new tuple.Tuple2(line.id, line.count)
      }.keyBy(0).addSink(...)

但是,由于我的文件夹中有一些旧数据,我不想处理它们。并且旧文件中记录的时间戳是> 6个小时,应该比水印旧。但是,当我开始运行它时,我仍然可以看到一些初始输出已经创建。我想知道如何设置水印的初始值,是在第一个间隔之前还是之后?可能是我在这里误解了一些东西但需要一些建议。

1 个答案:

答案 0 :(得分:1)

管道中没有运营商表明你关心时间 - 没有窗口,没有ProcessFunction定时器 - 所以每个流元素都会通过无阻碍的流程进行处理。如果您的目标是跳过迟到的元素,那么您需要引入一些(以某种方式)将事件时间戳与当前水印进行比较的内容。

你可以通过在keyBy和sink之间引入一个步骤来实现这一点,如下所示:

...
.keyBy(0)
.process(new DropLateEvents())
.addSink(...)

public static class DropLateEvents extends ProcessFunction<...> {
    @Override
    public void processElement(... event, Context context, Collector<...> out) throws Exception {
        TimerService timerService = context.timerService();
        if (context.timestamp() > timerService.currentWatermark()) {
           out.collect(event);
        }
    }
}

完成此操作后,您对初始水印的问题就变得相关了。对于周期性水印,初始水印为Long.MIN_VALUE,因此在发出第一个水印之前不会考虑任何后果,这将在操作10秒后发生(假设您已经设置了自动水印间隔)。 / p>

如果您想了解如何更详细地生成定期水印,相关代码为here

如果你想避免在前10秒内处理后期元素,你可以完全忘记使用事件时间和水印,只需修改上面显示的processElement方法,将事件时间戳与System.currentTimeMillis() - maxTimeLag进行比较,而不是到目前的水印。另一种解决方案是使用间断水印,并在第一次事件时发出水印。

或者甚至更简单地说,您可以在flatMap或过滤器中检测并删除延迟事件,因为您要定义相对于System.currentTimeMillis()而不是水印的延迟。