Fink时间语义
给定一个时间窗口,比如一个小时,统计时间窗口内的数据指标。如何界定哪些数据进入窗口?怎么定义这个时间?Flink提供了3种时间语义。
- Event Time:指数据流种每个元素或事件自带的事件属性,一般指事件发生事件。由于事件从发生到进入Flink算子之间有很多环节,使用Event Time意味着事件到达有可能是乱序的,没办法确定要等多长时间所有的事件是才能都到达,需要结合Watermark使用;
- Processing Time: 指使用算子当前节点的操作系统时间
- Ingestin Time: 是时间到达Flink Source的时间
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QNrNNWHl-1689168123798)(C:\Users\务本\AppData\Roaming\Typora\typora-user-images\image-20230611233609998.png)]](/https://i-blog.csdnimg.cn/blog_migrate/81f3b9dafe8a485525efa0c3e93bbfa1.png)
设置时间语义
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
Watermark
Watermark简单说就是一个时间戳,是尽量确保事件输入的完整性;为解决事件乱序问题,实现数据延迟和准确的平衡;
- 实现机制:数据进入flink,根据输入事件获取对应当前最大的时间戳,周期性的根据事件的最大时间戳生成waterMark发送出去,生成的waterMark过程可以设置延迟事件,waterMark再与时间窗口的数据对比,确认是否触发窗口计算;
/**
* {@code WatermarkGenerator} 可以基于事件或者周期性的生成 watermark。
*/
@Public
public interface WatermarkGenerator<T> {
/**
* 每来一条事件数据调用一次,可以检查或者记录事件的时间戳,或者也可以基于事件数据本身去生成 *watermark。
*/
void onEvent(T event, long eventTimestamp, WatermarkOutput output);
/**
* 周期性的调用,也许会生成新的 watermark,也许不会。
*
* <p>调用此方法生成 watermark 的间隔时间由 {@link ExecutionConfig#getAutoWatermarkInterval()} 决定。
*/
void onPeriodicEmit(WatermarkOutput output);
}
- Watermark 策略
- WatermarkStrategy.forMonotonousTimestamps():处理有序事件,按顺序递增,不加延迟
- WatermarkStrategy.forBoundedOutOfOrderness(Duration.ofSeconds(10)):无序事件,设置延迟时间
#设置Watermark策略
DataStream<MyType> stream = ...
DataStream<MyType> withTimestampsAndWatermarks = stream
.assignTimestampsAndWatermarks(
WatermarkStrategy
.forMonotonousTimestamps()
//提取事件内的时间戳
.withTimestampAssigner(...)
);
时间窗口类型
- 滚动窗口:窗口数据有固定的大小,窗口中的数据不会叠加;
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xPTlSa08-1689168123800)(C:\Users\务本\AppData\Roaming\Typora\typora-user-images\image-20230612161227747.png)]](/https://i-blog.csdnimg.cn/blog_migrate/37294a8b2b9ddd7969917d897ba6fbb8.png)
DataStream<T> input = ...
// tumbling event-time windows
input
.keyBy(<KeySelector>)
.window(TumblingEventTimeWindows.of(Time.seconds(5)))
.<window function>(...)
// tumbling processing-time windows
input
.keyBy(<KeySelector>)
.window(TumblingProcessingTimeWindows.of(Time.seconds(5)))
.<window function>(...)
// 1 hour tumbling event-time windows offset by 15 minutes.
input
.keyBy(<KeySelector>)
.window(TumblingEventTimeWindows.of(Time.hours(1), Time.minutes(15)))
.<window function>(...)
-
滑动窗口;窗口数据有固定的大小,并且有生成间隔;

-
会话窗口:窗口数据没有固定的大小,根据用户传入的参数进行划分,窗口数据无叠加。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VyoaWOxw-1689168123801)(C:\Users\务本\AppData\Roaming\Typora\typora-user-images\image-20230612161135012.png)]](/https://i-blog.csdnimg.cn/blog_migrate/3344bc42958446b1f9950f0a53f2e2bf.png)
时间窗口函数
- reduce()
- aggregate()
public interface AggregateFunction<IN, ACC, OUT>
extends Function, Serializable {
// 在一次新的Aggregate Function发起时,创建一个新的Accumulator,Accumulator中的值是我们所说的中间状态数据,即ACC数据
// 以下函数一般在初始化时调用
ACC createAccumulator();
// 当一个新元素流入时,将新元素与ACC数据合并,返回ACC数据
ACC add(IN value, ACC accumulator);
// 将两个ACC数据合并
ACC merge(ACC a, ACC b);
// 将中间数据转换成结果数据
OUT getResult(ACC accumulator);
}
- process()
/**
* 函数接收4个泛型
* IN:输入类型
* OUT:输出类型
* KEY:keyBy()中按照Key分组,Key类型
* W:窗口类型
*/
public abstract class ProcessWindowFunction<IN, OUT, KEY, W extends Window> extends
AbstractRichFunction {
/**
* 对一个窗口内的元素进行处理,窗口内的元素缓存在Iterable<IN>,进行处理后输出到
Collector<OUT>中
* 我们可以输出一到多个结果
*/
public abstract void process(KEY key, Context context, Iterable<IN> elements,
Collector<OUT> out) throws Exception;
/**
* 当窗口执行完毕被清理时,删除各类状态数据
*/
public void clear(Context context) throws Exception {}
/**
* 一个窗口的Context,包含窗口的一些元数据、状态数据等。
*/
public abstract class Context implements java.io.Serializable {
// 返回当前正在处理的窗口
public abstract W window();
// 返回当前Process Time
public abstract long currentProcessingTime();
// 返回当前Event Time对应的Watermark
public abstract long currentWatermark();
// 返回某个Key下的某个窗口的状态
public abstract KeyedStateStore windowState();
// 返回某个Key下的全局状态
public abstract KeyedStateStore globalState();
// 将迟到数据发送到其他位置
public abstract <X> void output(OutputTag<X> outputTag, X value);
}
}
双流连接(join)
类似mysql指定字段进行jion连接,只不过Flink在一个时间窗口上进行join;目前支持窗口连接和时间间隔连接
- 窗口连接(windown join)
input1.join(input2)
.where(<KeySelector>) <- input1 使用哪个字段作为Key
.equalTo(<KeySelector>) <- input2 使用哪个字段作为Key
.window(<WindowAssigner>) <- 指定WindowAssigner
[.trigger(<Trigger>)] <- 指定Trigger(可选)
[.evictor(<Evictor>)] <- 指定Evictor(可选)
.apply(<JoinFunction>) <- 指定JoinFunction
Join的大致过程,两个输入数据流先按key进行分组,再将元素划分到窗口,一个窗口内包含两个数据流的元素,以内连接来关联,形成一个数据对,最后调用JoinFunction对窗口的数据对进行处理;
- 时间间隔连接(Interval join)
public static class MyProcessFunction extends ProcessJoinFunction<Tuple3<String,
Long, Integer>, Tuple3<String, Long, Integer>, String> {
@Override
public void processElement(Tuple3<String, Long, Integer> input1,
Tuple3<String, Long, Integer> input2,
Context context,
Collector<String> out) {
out.collect("input 1: " + input1.toString() + ", input 2: " +
input2.toString());
}
}
// 使用Event Time
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
// 数据流有3个字段:(Key, 时间戳, 数值)
DataStream<Tuple3<String, Long, Integer>> input1 = ...
DataStream<Tuple3<String, Long, Integer>> input2 = ...
DataStream<String> intervalJoinResult = input1.keyBy(i -> i.f0)
.intervalJoin(input2.keyBy(i -> i.f0))
.between(Time.milliseconds(-5), Time.milliseconds(10))
.process(new MyProcessFunction());
迟到数据
- 直接丢弃,默认处理方法
- 则路输出
- 更新计算结果
final OutputTag<T> lateOutputTag = new OutputTag<T>("late-data"){};
DataStream<T> input = ...
SingleOutputStreamOperator<T> result = input
.keyBy(<key selector>)
.window(<window assigner>)
//设定允许迟到时间,更新数据
.allowedLateness(<time>)
//则路输出
.sideOutputLateData(lateOutputTag)
.<windowed transformation>(<window function>);
DataStream<T> lateStream = result.getSideOutput(lateOutputTag);

2946

被折叠的 条评论
为什么被折叠?



