版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
基本概念
- watermark 主要是为了解决数据乱序到达的问题
- allowed 解决窗口触发后数据迟到后的问题
能明白什么
- 窗口的范围
- 窗口的触发条件
- 窗口的销毁时间
- 迟到元素的处理方式
- watermark的生成方式
开始调试
- 代码准备 Flink
package com.wending.demo;
import com.alibaba.fastjson.JSONObject;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.TimeCharacteristic;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.AssignerWithPeriodicWatermarks;
import org.apache.flink.streaming.api.watermark.Watermark;
import org.apache.flink.streaming.api.windowing.assigners.TumblingEventTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer011;
import javax.annotation.Nullable;
/**
* date 2019/6/16
*/
public class KafkaWaterMark {
public static void main(String[] args) {
StreamExecutionEnvironment en = StreamExecutionEnvironment.getExecutionEnvironment();
en.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
en.getConfig().setAutoWatermarkInterval(5000);
en.enableCheckpointing(5000);
KafkaUtil util = new KafkaUtil();
// 自己封装
FlinkKafkaConsumer011 consumer = util.getConsumer("stream2", "test4");
en.setParallelism(1);
en.addSource(consumer).assignTimestampsAndWatermarks(new ass()).map(new MapFunction() {
@Override
public Tuple2<String, Integer> map(Object o) throws Exception {
String value = JSONObject.parseObject((String) o).getString("word");
System.out.println("value is " + value);
return new Tuple2<>(value, 1);
}
}).returns(Types.TUPLE(Types.STRING, Types.INT)).keyBy(0)
.window(TumblingEventTimeWindows.of(Time.seconds(10)))
.allowedLateness(Time.seconds(2)) // 最多允许迟到2s
.sum(1)
.print();
try {
en.execute("mark-test");
} catch (Exception e) {
e.printStackTrace();
}
}
}
class ass implements AssignerWithPeriodicWatermarks {
long maxOutOfOrderness = 6000;
private long currentMaxTimestamp;
@Override
public long extractTimestamp(Object element, long previousElementTimestamp) {
long createtime = 0L;
if (element != null) {
try {
createtime = JSONObject.parseObject((String) element).getLong("createTime");
System.out.println("extrac" + createtime);
} catch (Exception e) {
e.printStackTrace();
}
}
currentMaxTimestamp = Math.max(createtime, currentMaxTimestamp);
return createtime;
}
@Nullable
@Override
public Watermark getCurrentWatermark() {
System.out.println("watermark" + (currentMaxTimestamp - maxOutOfOrderness));
return new Watermark(currentMaxTimestamp - maxOutOfOrderness);
}
}
watermark 生成方式,抽取消费里的createTime,单调递增
- 代码准备 producer 自己实现吧,很简单的
- 调试结果
createtime | watermark | value | assign window | print result | note |
---|---|---|---|---|---|
1562419080000 (2019-07-06 21:18:00) | 1562419074000 | aa | [18:00-18:10) | aa | |
1562419085000 (2019-07-06 21:18:05) | 1562419079000 | aa | [18:00-18:10) | aa | |
1562419090000 (2019-07-06 21:18:10) | 1562419084000 | bb | [18:10-18:20) | bb | |
1562419089000 (2019-07-06 21:18:09) | 1562419084000 | bb | [18:00-18:10) | bb | |
1562419096000 (2019-07-06 21:18:16) | 1562419090000 | aa | [18:10-18:20) | (aa ,2) (bb,1) | 触发窗口计算了 watermark> window.max() |
1562419097000 (2019-07-06 21:18:17) | 1562419091000 | bb | [18:10-18:20) | bb | |
1562419080000 (2019-07-06 21:18:00) | 1562419091000 | aa | [18:00-18:10) | (aa,3) | 这是一个迟到的元素,此时的watermark还小于18:12 还能触发 |
1562419080000 (2019-07-06 21:18:00) | 1562419091000 | bb | [18:00-18:10) | (bb,2) | 这是一个迟到的元素,此时的watermark还小于18:12 还能触发 |
1562419098000 (2019-07-06 21:18:18) | 1562419092000 | bb | [18:10-18:20) | bb | 现在的watermark> 18:12 了 |
1562419080000 (2019-07-06 21:18:00) | 1562419092000 | aa | [18:00-18:10) | 触发不了,之前的窗口已经销毁了 |
总结
- 窗口的范围
以Ms,10s来看,一个窗口的范围就是1562419080000 - 1562419089999
- 窗口的触发条件
窗口内有数据,watermark>1562419089999 以就是窗口的max,才能触发,也就是说 window 在[18:00-18:10) 的数据实际上是得到creattime在18:16才能触发,所以说这个窗口到到达的数据 18:00<=creattime <18:10只需在18:00<=creattime <18:16到达即可,不用管顺序
- 窗口的销毁时间
如果没有设置allowedLateness,窗口触发就销毁,设置了的话,watermark>window.max+allowedLateness 即销毁
- 迟到元素的处理方式
可以设置allowedLateness,再等等迟到的元素
- watermark的生成方式
参考 生成watermark