Flink 10之 WaterMark机制原理
1 WaterMark的定义
使用eventTime的时候如何处理乱序数据?
我们知道,流处理从事件产生,到流经source,再到operator,中间是有一个过程和时间的。虽然大部
分情况下,流到operator的数据都是按照事件产生的时间顺序来的,但是也不排除由于网络延迟等原
因,导致乱序的产生,特别是使用kafka的话,多个分区的数据无法保证有序。所以在进行window计算
的时候,我们又不能无限期的等下去,必须要有个机制来保证一个特定的时间后,必须触发window去
进行计算了。这个特别的机制,就是watermark,watermark是用于处理乱序事件的。watermark可以
翻译为水位线
WaterMark是一种度量event Time进度的机制,
watermark作为数据流中的一部分在Stream中流动,
并携带time stamp 一个WaterMark(t) 表明在流中处理的EventTime已经到达了t,
那么在流中就不会再有Event Time小于 t的时间产生了。
有序的流的watermarks
多并行度流的watermarks
2 需求(深入理解WaterMark)
得到并打印每隔 3 秒钟统计相同的 key 的所有的事件(string)
相当于就是单词计数,每3s统计一次
背景:里面的数据可能就是乱序 = window + watermark
代码开发
/*** 得到并打印每隔 3 秒钟统计前 3 秒内的相同的 key 的所有的事件 3秒统计一卡。 单词计数 */
public class WaterMarkWindowWordCount {
public static void main(String[] args) throws Exception{
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
//步骤一:设置时间类型 env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
//设置waterMark产生的周期为1s
env.getConfig().setAutoWatermarkInterval(1000);
DataStreamSource<String> dataStream = env.socketTextStream("hadoop1", 8888);
dataStream.map(new MapFunction<String, Tuple2<String,Long>>() {
@Override public Tuple2<String, Long> map(String line) throws Exception {
String[] fields = line.split(",");
return new Tuple2<>(fields[0],Long.valueOf(fields[1]));
}
//步骤二:获取数据里面的event Time
}).assignTimestampsAndWatermarks(new EventTimeExtractor() )
.keyBy(0)
.timeWindow(Time.seconds(3))
.process(new SumProcessWindowFunction())
.print().setParallelism(1);
env.execute("TimeWindowWordCount");
}
/**
* IN, OUT, KEY, W
* IN:输入的数据类型
* OUT:输出的数据类型
* Key:key的数据类型(在Flink里面,String用Tuple表示)
* W:Window的数据类型 */
public static class SumProcessWindowFunction extends ProcessWindowFunction<Tuple2<String,Long>,String,Tuple,TimeWindow> {
FastDateFormat dateFormat = FastDateFormat.getInstance("HH:mm:ss");
/**
* 当一个window触发计算的时候会调用这个方法
* @param tuple key
* @param context operator的上下文
* @param elements 指定window的所有元素
* @param out 用户输出 */
@Override public void process(Tuple tuple, Context context, Iterable<Tuple2<String, Long>> elements, Collector<String> out) {
System.out.println("处理时间:" + dateFormat.format(context.currentProcessingTime()));
System.out.println("window start time : " + dateFormat.format(context.window().getStart()));
List<String> list = new ArrayList<>();
for (Tuple2<String, Long> ele : elements) {
list.add(ele.toString() + "|" + dateFormat.format(ele.f1));
}
out.collect(list.toString());
System.out.println("window end time : " + dateFormat.format(context.window().getEnd()));
}
}
private static class EventTimeExtractor implements AssignerWithPeriodicWatermarks<Tuple2<String, Long>> {
FastDateFormat dateFormat = FastDateFormat.getInstance("HH:mm:ss");
private long currentMaxEventTime = 0L;
private long maxOutOfOrderness = 10000;
// 最大允许的乱序时间 1