用例:使用EventTime并从Kafka的记录中提取时间戳。
myConsumer.assignTimestampsAndWatermarks(new MyTimestampEmitter());
...
stream
.keyBy("platform")
.window(TumblingEventTimeWindows 5 mins))
.aggregate(AggFunc(), WindowFunc())
.countWindowAll(size)
.apply(someFunc)
.addSink(someSink);
我想要的:Flink提取时间戳并在初始间隔(例如20秒)内为每条记录发出水印,然后可以定期发出水印(例如每10秒)。
原因::如果我使用了 PeriodicWatermark ,则开始时Flink仅在一定间隔后才会发出水印,并且在我的第一个5分钟窗口中的计数是错误的-更大比后续窗口中的计数多。我有一种解决方法,将setAutoWatermarkInterval设置为100ms,但这超出了必要。
当前,我必须使用 AssignerWithPeriodicWatermark 或 AssignerWithPunctuatedWatermark 。如何实施这种合并策略方法?谢谢。
答案 0 :(得分:1)
在对水印生成器进行异常处理之前,我将仔细检查您是否已正确诊断了这种情况。总体而言,事件时间窗口应具有确定性,并且如果显示相同的输入,则始终会产生相同的结果。如果您获得的第一个窗口的结果根据生成水印的频率而有所不同,则表明您可能有较晚的事件,这些事件在水印到达频率更高时被丢弃,并且在水印较少时可以被包含在内频繁。也许您的水印未正确说明事件所经历的实际乱序程度?还是您的水印是基于System.currentTimeMillis(),而不是事件时间戳?
而且,第一次时间窗口与其他时间窗口不同是很正常的,因为时间窗口是与纪元而不是第一次事件对齐的。当然,这样做的结果是,第一个窗口所占的时间比所有其他窗口都短,因此您应该期望它包含的事件更少,而不是更多。
将setAutoWatermarkInterval设置为100ms是一件很正常的事情。但是,如果您真的想避免这种情况,则可以考虑使用AssignerWithPunctuatedWatermarks,它最初为每个事件返回一个水印,然后在适当的时间间隔之后,不那么频繁地返回水印。
在标点符号水印分配器中,每个事件都会调用extractTimestamp和checkAndGetNextWatermark方法。您可以在分配器中使用一些瞬态(非链接)状态来跟踪第一个事件的时间或对事件进行计数,然后在checkAndGetNextWatermark中使用该信息来最终退出并停止为每个事件生成水印(通过有时从checkAndGetNextWatermark(而不是Watermark)返回null。每当重新启动应用程序时,您的应用程序将始终恢复为每个事件生成水印。
这不会产生具有周期性和标点分配器所有特征的分配器,而只是一个自适应的标点分配器。