Flink Streaming基于滚动窗口的事件时间分析
使用flink-1.9.0进行的测试,在不同的并行度下,Flink对事件时间的处理逻辑不同。包括1.1在并行度为1的本地模式分析和1.2在多并行度的本地模式分析两部分。通过理论结合源码进行验证,得到具有说服力的结论。
一、使用并行度为1的本地模式测试
1.1、Flink时间时间窗口代码,使用SocketSource:
package com.mengyao.flink.stream.window; import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List; import org.apache.flink.api.common.restartstrategy.RestartStrategies;
import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.api.java.tuple.Tuple;
import org.apache.flink.api.java.tuple.Tuple4;
import org.apache.flink.configuration.ConfigConstants;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.configuration.RestOptions;
import org.apache.flink.streaming.api.TimeCharacteristic;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.AssignerWithPeriodicWatermarks;
import org.apache.flink.streaming.api.functions.windowing.WindowFunction;
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.api.windowing.windows.TimeWindow;
import org.apache.flink.util.Collector; import com.mengyao.flink.stream.utils.DateUtil; /**
* 启动netcat:nc -L -p 9999 -v
*
* Created by: mengyao
* 2019年10月15日
*/
public class SocketEventTimeWindowApp { private static String jobName = SocketEventTimeWindowApp.class.getSimpleName(); public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
conf.setBoolean(ConfigConstants.LOCAL_START_WEBSERVER, true);
conf.setInteger(RestOptions.PORT, RestOptions.PORT.defaultValue());
final StreamExecutionEnvironment env = StreamExecutionEnvironment.createLocalEnvironment(1, conf);
// 设置重启策略,5次尝试,每次尝试的间隔为30秒
env.setRestartStrategy(RestartStrategies.fixedDelayRestart(5, 30000));
// 使用事件时间
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
// 数据处理
DataStream<Tuple4<String, String, String, Long>> inputDS = env.socketTextStream("localhost", 9999)
.map(line -> {
String[] fields = line.split(",",3);
return Tuple4.of(fields[0], fields[1], fields[2], DateUtil.FMT05.get().parse((fields[2])).getTime());
})
.returns(Types.TUPLE(Types.STRING, Types.STRING, Types.STRING, Types.LONG))
.assignTimestampsAndWatermarks(new AssignerWithPeriodicWatermarks<Tuple4<String, String, String, Long>>() {// 分配时间戳并定期生成水印
private static final long serialVersionUID = -4195773369390603522L;
private SimpleDateFormat formatter = DateUtil.FMT15.get();
long currentMaxTimestamp = 0L;
long maxOutOfOrderness = 0L;//允许最大的乱序时间是0秒
@Override
public long extractTimestamp(Tuple4<String, String, String, Long> ele, long prevEleTs) {
long timestamp = ele.f3;
currentMaxTimestamp = Math.max(timestamp, currentMaxTimestamp);
System.out.println("事件: "+ele+", 最大时间戳:"+DateUtil.ts2DateStr(currentMaxTimestamp, formatter)+", 水印时间戳:"+DateUtil.ts2DateStr(getCurrentWatermark().getTimestamp(), formatter));
return timestamp;
}
@Override
public Watermark getCurrentWatermark() {return new Watermark(currentMaxTimestamp - maxOutOfOrderness);}
}); inputDS
.keyBy(0)
.window(TumblingEventTimeWindows.of(Time.seconds(2)))// 使用滚动窗口
.apply(new WindowFunction<Tuple4<String,String,String,Long>, String, Tuple, TimeWindow>() {
private static final long serialVersionUID = -4990083905742822422L;
private SimpleDateFormat formatter = DateUtil.FMT15.get();
@Override
public void apply(Tuple key, TimeWindow window, Iterable<Tuple4<String, String, String, Long>> input,
Collector<String> out) throws Exception {
// 按照事件时间升序排序
List<Tuple4<String, String, String, Long>> list = new ArrayList<>();
input.forEach(t4->list.add(t4));
list.sort((e1,e2)->e1.f3.compareTo(e2.f3));
System.out.println("==== "+key+", 窗口开始:"+DateUtil.ts2DateStr(window.getStart(), formatter)+",窗口结束:"+DateUtil.ts2DateStr(window.getEnd(), formatter)+"; 窗口内的数据:"+list);
}
})
.print(); env.execute(jobName);
} }
1.2、使用netcat启动SocketServer,发送数据到FlinkStreaming中(数据是有序的情况下)
C:\Users\mengyao>nc -l -p 9999 -v
listening on [any] 9999 ...
connect to [127.0.0.1] from DESKTOP-H7J35OJ [127.0.0.1] 63187
1,1,20190101090000000
1,1,20190101090001000
1,1,20190101090001999
1,1,20190101090002000
1,1,20190101090003000
1,1,20190101090004000
1,1,20190101090005000
1,1,20190101090005500
1,1,20190101090007000
1.3、程序控制台输出:
log4j:WARN No appenders could be found for logger (org.apache.flink.api.java.ClosureCleaner).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
事件: (1,1,20190101090000000,1546304400000), 最大时间戳:2019-01-01 09:00:00.000, 水印时间戳:2019-01-01 09:00:00.000
事件: (1,1,20190101090001000,1546304401000), 最大时间戳:2019-01-01 09:00:01.000, 水印时间戳:2019-01-01 09:00:01.000
事件: (1,1,20190101090001999,1546304401999), 最大时间戳:2019-01-01 09:00:01.999, 水印时间戳:2019-01-01 09:00:01.999
==== (1), 窗口开始:2019-01-01 09:00:00.000,窗口结束:2019-01-01 09:00:02.000; 窗口内的数据:[(1,1,20190101090000000,1546304400000), (1,1,20190101090001000,1546304401000), (1,1,20190101090001999,1546304401999)]
事件: (1,1,20190101090002000,1546304402000), 最大时间戳:2019-01-01 09:00:02.000, 水印时间戳:2019-01-01 09:00:02.000
事件: (1,1,20190101090003000,1546304403000), 最大时间戳:2019-01-01 09:00:03.000, 水印时间戳:2019-01-01 09:00:03.000
事件: (1,1,20190101090004000,1546304404000), 最大时间戳:2019-01-01 09:00:04.000, 水印时间戳:2019-01-01 09:00:04.000
==== (1), 窗口开始:2019-01-01 09:00:02.000,窗口结束:2019-01-01 09:00:04.000; 窗口内的数据:[(1,1,20190101090002000,1546304402000), (1,1,20190101090003000,1546304403000)]
事件: (1,1,20190101090005000,1546304405000), 最大时间戳:2019-01-01 09:00:05.000, 水印时间戳:2019-01-01 09:00:05.000
事件: (1,1,20190101090005500,1546304405500), 最大时间戳:2019-01-01 09:00:05.500, 水印时间戳:2019-01-01 09:00:05.500
事件: (1,1,20190101090007000,1546304407000), 最大时间戳:2019-01-01 09:00:07.000, 水印时间戳:2019-01-01 09:00:07.000
==== (1), 窗口开始:2019-01-01 09:00:04.000,窗口结束:2019-01-01 09:00:06.000; 窗口内的数据:[(1,1,20190101090004000,1546304404000), (1,1,20190101090005000,1546304405000), (1,1,20190101090005500,1546304405500)]
1.4、滚动窗口分析:
控制台打印如下: |
|
解释: |
|
窗口的开始时间:2019-01-01 09:00:00.000 |
窗口开始时间由事件数据决定,即接收到第一条事件数据是:(1,1,20190101090000000,1546304400000),所以窗口开始时间为:20190101090000000。 |
窗口的结束时间:2019-01-01 09:00:02.000 |
窗口开始时间是2019-01-01 09:00:00.000,窗口长度是2秒,那窗口结束时间 = 窗口开始时间+2秒 = 2019-01-01 09:00:02.000。 |
窗口的长度:2秒 |
在1.1代码的72行:window(TumblingEventTimeWindows.of(Time.seconds(2)))。使用滚动窗口,且窗口长度为2秒。 |
进入窗口的数据:[ |
因为窗口属于左闭右开(包前不包后),所以这个窗口的时间范围是从2019-01-01 09:00:00.000 到 2019-01-01 09:00:02.000 - 1。只要数据的事件时间属于该区间就会落在这个窗口中。 |
窗口的水印时间:因延迟时间为0,所以水印时间 = 事件时间。 |
在1.1代码的67行,public Watermark getCurrentWatermark() {return new Watermark(currentMaxTimestamp - maxOutOfOrderness);} |
窗口的触发时机:窗口结束时间-1 |
源码如下(TriggerResult枚举类的FIRE状态表示将窗口求值并发出结果,不清除窗口数据,会保留所有元素)。 |
第二次触发窗口计算
控制台打印如下: |
|
解释: |
|
窗口的开始时间:2019-01-01 09:00:02.000 |
窗口开始时间由事件数据决定,即接收到第一条事件数据是:(1,1,20190101090002000,1546304402000),所以窗口开始时间为:20190101090002000。 |
窗口的结束时间:2019-01-01 09:00:04.000 |
窗口开始时间是2019-01-01 09:00:02.000,窗口长度是2秒,那窗口结束时间 = 窗口开始时间+2秒 = 2019-01-01 09:00:04.000。 |
窗口的长度:2秒 |
在1.1代码的72行:window(TumblingEventTimeWindows.of(Time.seconds(2)))。使用滚动窗口,且窗口长度为2秒。 |
进入窗口的数据:[ |
因为窗口属于左闭右开(包前不包后),所以这个窗口的时间范围是从2019-01-01 09:00:02.000 到 2019-01-01 09:00:04.000 - 1。只要数据的事件时间属于该区间就会落在这个窗口中。 |
窗口的水印时间:因延迟时间为0,所以事件时间 = 水印时间。 |
在1.1代码的67行,public Watermark getCurrentWatermark() {return new Watermark(currentMaxTimestamp - maxOutOfOrderness);} |
窗口的触发时机:窗口结束时间-1 |
源码如下(TriggerResult枚举类的FIRE状态表示将窗口求值并发出结果,不清除窗口数据,会保留所有元素)。 |
第三次触发窗口计算:
控制台打印如下: |
|
解释: |
|
窗口的开始时间:2019-01-01 09:00:04.000 |
窗口开始时间由事件数据决定,即接收到第一条事件数据是:(1,1,20190101090004000,1546304404000),所以窗口开始时间为:20190101090004000。 |
窗口的结束时间:2019-01-01 09:00:06.000 |
窗口开始时间是2019-01-01 09:00:04.000,窗口长度是2秒,那窗口结束时间 = 窗口开始时间+2秒 = 2019-01-01 09:00:06.000。 |
窗口的长度:2秒 |
在1.1代码的72行:window(TumblingEventTimeWindows.of(Time.seconds(2)))。使用滚动窗口,且窗口长度为2秒。 |
进入窗口的数据:[ |
因为窗口属于左闭右开(包前不包后),所以这个窗口的时间范围是从2019-01-01 09:00:04.000 到 2019-01-01 09:00:06.000 - 1。只要数据的事件时间属于该区间就会落在这个窗口中。 |
窗口的水印时间:因延迟时间为0,所以事件时间 = 水印时间。 |
在1.1代码的67行,public Watermark getCurrentWatermark() {return new Watermark(currentMaxTimestamp - maxOutOfOrderness);} |
窗口的触发时机:窗口结束时间-1 |
源码如下(TriggerResult枚举类的FIRE状态表示将窗口求值并发出结果,不清除窗口数据,会保留所有元素)。 |
总结:
先上图
=================================================
这3个窗口的规律总结为:
1、水印时间:事件时间 - 允许的最大乱序时间0秒,即每个水印时间落后于事件时间0秒,代码:new Watermark(currentMaxTimestamp - maxOutOfOrderness)。
2、窗口的开始时间:基于事件时间的滚动窗口下,是以(第一条数据的事件时间 or 事件时间大于等于前一个窗口的数据)作为窗口的开始时间,而不是算子所处节点的系统时钟。
3、窗口的结束时间:基于事件时间的滚动窗口下,窗口结束时间 = 窗口开始时间 + 窗口的长度即TumblingEventTimeWindows.of(Time.seconds(2))。
4、窗口的计算时机:数据的水印时间 >= 窗口结束时间 or 数据的水印时间 = 窗口结束时间-1毫秒时,就触发了窗口的计算。
5、落入窗口内的数据:窗口属于左闭右开,即数据的事件时间 >= 窗口起始时间 并且 < 窗口的结束时间。
二、使用并行度为4(多并行度)的本地模式测试
2.1、Flink时间时间窗口代码,使用SocketSource:
package com.mengyao.flink.stream.window; import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List; import org.apache.flink.api.common.restartstrategy.RestartStrategies;
import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.api.java.tuple.Tuple;
import org.apache.flink.api.java.tuple.Tuple4;
import org.apache.flink.configuration.ConfigConstants;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.configuration.RestOptions;
import org.apache.flink.streaming.api.TimeCharacteristic;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.AssignerWithPeriodicWatermarks;
import org.apache.flink.streaming.api.functions.windowing.WindowFunction;
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.api.windowing.windows.TimeWindow;
import org.apache.flink.util.Collector; import com.mengyao.flink.stream.utils.DateUtil; /**
* 启动netcat:nc -L -p 9999 -v
* 验证多并行度时的事件时间窗口。
* Created by: mengyao
* 2019年10月15日
*/
public class MultipleParallelismSocketEventTimeWindowApp { private static String jobName = MultipleParallelismSocketEventTimeWindowApp.class.getSimpleName(); public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
conf.setBoolean(ConfigConstants.LOCAL_START_WEBSERVER, true);
conf.setInteger(RestOptions.PORT, RestOptions.PORT.defaultValue());
final StreamExecutionEnvironment env = StreamExecutionEnvironment.createLocalEnvironment(4, conf);
// 设置重启策略,5次尝试,每次尝试的间隔为30秒
env.setRestartStrategy(RestartStrategies.fixedDelayRestart(5, 30000));
// 使用事件时间
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
// 数据处理
DataStream<Tuple4<String, String, String, Long>> inputDS = env.socketTextStream("localhost", 9999)
.map(line -> {
String[] fields = line.split(",",3);
return Tuple4.of(fields[0], fields[1], fields[2], DateUtil.FMT05.get().parse((fields[2])).getTime());
})
.returns(Types.TUPLE(Types.STRING, Types.STRING, Types.STRING, Types.LONG))
.assignTimestampsAndWatermarks(new AssignerWithPeriodicWatermarks<Tuple4<String, String, String, Long>>() {// 分配时间戳并定期生成水印
private static final long serialVersionUID = -4195773369390603522L;
private SimpleDateFormat formatter = DateUtil.FMT15.get();
long currentMaxTimestamp = 0L;
long maxOutOfOrderness = 000L;//允许最大的乱序时间是5秒
@Override
public long extractTimestamp(Tuple4<String, String, String, Long> ele, long prevEleTs) {
long timestamp = ele.f3;
currentMaxTimestamp = Math.max(timestamp, currentMaxTimestamp);
long tId = Thread.currentThread().getId();
System.out.println("线程ID:"+tId+", 事件: "+ele+", 最大时间戳:"+DateUtil.ts2DateStr(currentMaxTimestamp, formatter)+", 水印时间戳:"+DateUtil.ts2DateStr(getCurrentWatermark().getTimestamp(), formatter));
return timestamp;
}
@Override
public Watermark getCurrentWatermark() {return new Watermark(currentMaxTimestamp - maxOutOfOrderness);}
}); inputDS
.keyBy(0)
.window(TumblingEventTimeWindows.of(Time.seconds(2)))// 使用滚动窗口
.apply(new WindowFunction<Tuple4<String,String,String,Long>, String, Tuple, TimeWindow>() {
private static final long serialVersionUID = -4990083905742822422L;
private SimpleDateFormat formatter = DateUtil.FMT15.get();
@Override
public void apply(Tuple key, TimeWindow window, Iterable<Tuple4<String, String, String, Long>> input,
Collector<String> out) throws Exception {
// 按照事件时间升序排序
List<Tuple4<String, String, String, Long>> list = new ArrayList<>();
input.forEach(t4->list.add(t4));
list.sort((e1,e2)->e1.f3.compareTo(e2.f3));
long tId = Thread.currentThread().getId();
System.out.println("==== 线程ID:"+tId+",key="+key+", 窗口开始:"+DateUtil.ts2DateStr(window.getStart(), formatter)+",窗口结束:"+DateUtil.ts2DateStr(window.getEnd(), formatter)+"; 窗口内的数据:"+list);
}
})
.print(); env.execute(jobName);
} }
2.2、使用netcat启动SocketServer,发送数据到FlinkStreaming中
listening on [127.0.0.1] 9999 ...
connect to [127.0.0.1] from DESKTOP-H7J35OJ [127.0.0.1] 59356
1,1,20190101090000000
1,1,20190101090000500
1,1,20190101090001000
1,1,20190101090001999
1,1,20190101090002000
1,1,20190101090002500
1,1,20190101090001999
1,1,20190101090002300
1,1,20190101090003000
1,1,20190101090003999
1,1,20190101090004000
1,1,20190101090004500
1,1,20190101090003999
1,1,20190101090005999
1,1,20190101090005999
1,1,20190101090005999
1,1,20190101090005999
2.3、程序控制台输出:
线程ID:63, 事件: (1,1,20190101090000000,1546304400000), 最大时间戳:2019-01-01 09:00:00.000, 水印时间戳:2019-01-01 09:00:00.000
线程ID:64, 事件: (1,1,20190101090000500,1546304400500), 最大时间戳:2019-01-01 09:00:00.500, 水印时间戳:2019-01-01 09:00:00.500
线程ID:65, 事件: (1,1,20190101090001000,1546304401000), 最大时间戳:2019-01-01 09:00:01.000, 水印时间戳:2019-01-01 09:00:01.000
线程ID:66, 事件: (1,1,20190101090001999,1546304401999), 最大时间戳:2019-01-01 09:00:01.999, 水印时间戳:2019-01-01 09:00:01.999
线程ID:63, 事件: (1,1,20190101090002000,1546304402000), 最大时间戳:2019-01-01 09:00:02.000, 水印时间戳:2019-01-01 09:00:02.000
线程ID:64, 事件: (1,1,20190101090002500,1546304402500), 最大时间戳:2019-01-01 09:00:02.500, 水印时间戳:2019-01-01 09:00:02.500
线程ID:65, 事件: (1,1,20190101090001999,1546304401999), 最大时间戳:2019-01-01 09:00:01.999, 水印时间戳:2019-01-01 09:00:01.999
==== 线程ID:72,key=(1), 窗口开始:2019-01-01 09:00:00.000,窗口结束:2019-01-01 09:00:02.000; 窗口内的数据:[(1,1,20190101090000000,1546304400000), (1,1,20190101090000500,1546304400500), (1,1,20190101090001000,1546304401000), (1,1,20190101090001999,1546304401999), (1,1,20190101090001999,1546304401999)]
线程ID:66, 事件: (1,1,20190101090002300,1546304402300), 最大时间戳:2019-01-01 09:00:02.300, 水印时间戳:2019-01-01 09:00:02.300
线程ID:63, 事件: (1,1,20190101090003000,1546304403000), 最大时间戳:2019-01-01 09:00:03.000, 水印时间戳:2019-01-01 09:00:03.000
线程ID:64, 事件: (1,1,20190101090003999,1546304403999), 最大时间戳:2019-01-01 09:00:03.999, 水印时间戳:2019-01-01 09:00:03.999
线程ID:65, 事件: (1,1,20190101090004000,1546304404000), 最大时间戳:2019-01-01 09:00:04.000, 水印时间戳:2019-01-01 09:00:04.000
线程ID:66, 事件: (1,1,20190101090004500,1546304404500), 最大时间戳:2019-01-01 09:00:04.500, 水印时间戳:2019-01-01 09:00:04.500
线程ID:63, 事件: (1,1,20190101090003999,1546304403999), 最大时间戳:2019-01-01 09:00:03.999, 水印时间戳:2019-01-01 09:00:03.999
==== 线程ID:72,key=(1), 窗口开始:2019-01-01 09:00:02.000,窗口结束:2019-01-01 09:00:04.000; 窗口内的数据:[(1,1,20190101090002000,1546304402000), (1,1,20190101090002300,1546304402300), (1,1,20190101090002500,1546304402500), (1,1,20190101090003000,1546304403000), (1,1,20190101090003999,1546304403999), (1,1,20190101090003999,1546304403999)]
线程ID:64, 事件: (1,1,20190101090005999,1546304405999), 最大时间戳:2019-01-01 09:00:05.999, 水印时间戳:2019-01-01 09:00:05.999
线程ID:65, 事件: (1,1,20190101090005999,1546304405999), 最大时间戳:2019-01-01 09:00:05.999, 水印时间戳:2019-01-01 09:00:05.999
线程ID:66, 事件: (1,1,20190101090005999,1546304405999), 最大时间戳:2019-01-01 09:00:05.999, 水印时间戳:2019-01-01 09:00:05.999
线程ID:63, 事件: (1,1,20190101090005999,1546304405999), 最大时间戳:2019-01-01 09:00:05.999, 水印时间戳:2019-01-01 09:00:05.999
==== 线程ID:72,key=(1), 窗口开始:2019-01-01 09:00:04.000,窗口结束:2019-01-01 09:00:06.000; 窗口内的数据:[(1,1,20190101090004000,1546304404000), (1,1,20190101090004500,1546304404500), (1,1,20190101090005999,1546304405999), (1,1,20190101090005999,1546304405999), (1,1,20190101090005999,1546304405999), (1,1,20190101090005999,1546304405999)]
2.4、滚动窗口分析:
第一次窗口触发(水印时间:2019-01-01 09:00:01.999)
控制台打印如下:
==== 线程ID:72,key=(1), 窗口开始:2019-01-01 09:00:00.000,窗口结束:2019-01-01 09:00:02.000; 窗口内的数据:[(1,1,20190101090000000,1546304400000), (1,1,20190101090000500,1546304400500), (1,1,20190101090001000,1546304401000), (1,1,20190101090001999,1546304401999), (1,1,20190101090001999,1546304401999)]
解释:
当每一个线程中最新的水印时间都 > = 窗口结束时间 / 窗口结束时间-1毫秒时,就会触发窗口的计算。
第二次窗口触发(水印时间:2019-01-01 09:00:03.999)
控制台打印如下:
==== 线程ID:72,key=(1), 窗口开始:2019-01-01 09:00:02.000,窗口结束:2019-01-01 09:00:04.000; 窗口内的数据:[(1,1,20190101090002000,1546304402000), (1,1,20190101090002300,1546304402300), (1,1,20190101090002500,1546304402500), (1,1,20190101090003000,1546304403000), (1,1,20190101090003999,1546304403999), (1,1,20190101090003999,1546304403999)]
解释:
当每一个线程中最新的水印时间都 > = 窗口结束时间 / 窗口结束时间-1毫秒时,就会触发窗口的计算。
第三次窗口触发(水印时间:2019-01-01 09:00:05.999)
控制台打印如下:
==== 线程ID:72,key=(1), 窗口开始:2019-01-01 09:00:04.000,窗口结束:2019-01-01 09:00:06.000; 窗口内的数据:[(1,1,20190101090004000,1546304404000), (1,1,20190101090004500,1546304404500), (1,1,20190101090005999,1546304405999), (1,1,20190101090005999,1546304405999), (1,1,20190101090005999,1546304405999), (1,1,20190101090005999,1546304405999)]
解释:
当每一个线程中最新的水印时间都 > = 窗口结束时间 / 窗口结束时间-1毫秒时,就会触发窗口的计算。
多并行度下事件时间窗口总结:
1、当所有线程内的最新水印时间 >= 窗口结束时间 / 窗口结束时间-1。 就会触发窗口的计算。
Flink Streaming基于滚动窗口的事件时间分析的更多相关文章
- Apache Flink -Streaming(DataStream API)
综述: 在Flink中DataStream程序是在数据流上实现了转换的常规程序. 1.示范程序 import org.apache.flink.api.common.functions.FlatMap ...
- 【源码解析】Flink 是如何基于事件时间生成Timestamp和Watermark
生成Timestamp和Watermark 的三个重载方法介绍可参见上一篇博客: Flink assignAscendingTimestamps 生成水印的三个重载方法 之前想研究下Flink是怎么处 ...
- 「Flink」事件时间与水印
我们先来以滚动时间窗口为例,来看一下窗口的几个时间参数与Flink流处理系统时间特性的关系. 获取窗口开始时间Flink源代码 获取窗口的开始时间为以下代码: org.apache.flink.str ...
- Flink架构(三)- 事件-时间(Event-Time)处理
3. 事件-时间(Event-Time)处理 在“时间语义”中,我们强调了在流处理应用中时间语义的重要性,并解释了处理时间与事件时间的不同点.处理时间较好理解,因为它基于本地机器的时间,它产生的是有点 ...
- 2,StructuredStreaming的事件时间和窗口操作
推荐阅读:1,StructuredStreaming简介 使用Structured Streaming基于事件时间的滑动窗口的聚合操作是很简单的,很像分组聚合.在一个分组聚合操作中,聚合值被唯一保存在 ...
- js进阶 12-6 监听鼠标滚动事件和窗口改变事件怎么写
js进阶 12-6 监听鼠标滚动事件和窗口改变事件怎么写 一.总结 一句话总结:滚动事件scroll(),浏览器窗口调整监听resize(),思考好监听对象. 1.滚动事件scroll()的监听对象是 ...
- 事件时间(event time)与水印(watermark)
事件时间和水印诞生的背景 在实际的流式计算中数据到来的顺序对计算结果的正确性有至关重要的影响 比如:某数据源中的某些数据由于某种原因(如:网络原因,外部存储自身原因)会有2秒的延时,也就是在实际时间的 ...
- 纯c++实现之滚动窗口
别在MFC了,先分析下,上图 我们以左上角为坐标原点,用position_width和position_height来保存当前显示坐标. 根据msdn说明,滚动条默认情况下的值在0~100之间. 根据 ...
- Atitit事件代理机制原理 基于css class的事件代理
Atitit事件代理机制原理 基于css class的事件代理 1.1. 在javasript中delegate这个词经常出现,看字面的意思,代理.委托1 1.2. 事件代理1 1.3. 代理标准化规 ...
随机推荐
- Web服务器、应用程序服务器、web应用服务器、反向代理服务器
参考链接:https://www.cnblogs.com/zhaoyl/archive/2012/10/10/2718575.html 首先我们来了解什么是服务器(server) 一般来说,serve ...
- 【Python学习之四】集合类型
环境 虚拟机:VMware 10 Linux版本:CentOS-6.5-x86_64 客户端:Xshell4 FTP:Xftp4 python3.6 一.字符串:字符串实际上就是字符的数组1.切片是指 ...
- IDEA进行activiti-archetype-unittest脚手架的安装
官网:https://www.activiti.org/ 第一步:下载activiti源码(https://github.com/Activiti/Activiti/tags) 第二步:在termin ...
- nuxt/eapress 安装报错Module build failed: ValidationError: PostCSS Loader Invalid OptionsModule build failed: ValidationError: PostCSS Loader Invalid Options options['useConfigFile'] is an invalid additi
错误信息: Module build failed: ValidationError: PostCSS Loader Invalid Options options['useConfigFile'] ...
- python面试题300道
本文截取了一些面试题及解决方案: Python 基础 文件操作 模块与包 数据类型 企业面试题 Python 高级 设计模式 系统编程 Python 基础 什么是 Python?根据Python 创建 ...
- OpenJudge 4152 最佳加法表达式
总时间限制: 1000ms 内存限制: 65536kB 描述 给定n个1到9的数字,要求在数字之间摆放m个加号(加号两边必须有数字),使得所得到的加法表达式的值最小,并输出该值.例如,在1234中摆放 ...
- nodepad++格式化html代码
如果没有安装插件
- C++Primer 5th Chap4 Expressions
左值和右值:左值:用的是对象的身份(内存中的位置),右值:用的是对象的值(内容) 解引用与递增(递减)运算符连用: *ivec++:取ivec当前值并向后移动一个元素,等价于*(ivec++),本来+ ...
- LeetCode第152场周赛(Java)
这算是我第一次正式参加 LeetCode 的周赛吧.通过两道题.意料之中(通过上次模拟可以看出来).总的来说,脑袋还是不太灵光.想的有点慢.全球第一名 0:10:19 就全部通过...感觉我的智商被狠 ...
- max_prepared_stmt_count参数
MySQL报错[mysqld-5.5.17-log]Can't create more than max_prepared_stmt_count statements (current value: ...