使用事件时间时,需要通过 Flink API 的 WatermarkStrategy
接口配置 watermark 的生成策略。
我们将逐段来看这个 API 的各个部分。
继承关系
WatermarkStrategy
接口继承了 TimestampAssignerSupplier
和 WatermarkGeneratorSupplier
,即相当于包含了 TimestampAssigner
和 WatermarkGenerator
,具体地:
TimestampAssigner
用于从消息记录中提取事件时间戳WatermarkGenerator
用于根据事件时间戳生成 watermark
源码:
flink-core/src/main/java/org/apache/flink/api/common/eventtime/WatermarkStrategy.java#L19
【Github】package org.apache.flink.api.common.eventtime; import org.apache.flink.annotation.Public; import org.apache.flink.annotation.PublicEvolving; import java.io.Serializable; import java.time.Duration; import static org.apache.flink.util.Preconditions.checkArgument; import static org.apache.flink.util.Preconditions.checkNotNull; @Public public interface WatermarkStrategy<T> extends TimestampAssignerSupplier<T>, WatermarkGeneratorSupplier<T> {
其中,泛型
T
为构造的WatermarkGenerator
处理的输入数据流类型。
TimestampAssignerSupplier
和 WatermarkGeneratorSupplier
均继承了 Serializable
,所以WatermarkStrategy
也继承了 Serializable
,是可序列化的。这是因为在分布式计算过程中,WatermarkStrategy
可能会在不同节点之间传输。
接口方法
需要实现或重写的方法
实现 WatermarkStrategy
只需要实现 createWatermarkGenerator
方法,也可以根据需要重写 createTimestampAssigner
和 getAlignmentParameters
方法。
WatermarkStrategy
接口需要实现如下 3 个方法:
createWatermarkGenerator
:返回WatermarkGenerator
实例,这个方法没有默认实现createTimestampAssigner
:返回TimestampAssigner
实例,这个方法的默认实现返回RecordTimestampAssigner
类的实例getAlignmentParameters
:返回控制 watermark 对齐的参数实例,这个方法的默认实现返回不需要对齐 watermark 的参数实例
源码:
flink-core/src/main/java/org/apache/flink/api/common/eventtime/WatermarkStrategy.java#L59
【Github】@Override WatermarkGenerator<T> createWatermarkGenerator(WatermarkGeneratorSupplier.Context context); @Override default TimestampAssigner<T> createTimestampAssigner(TimestampAssignerSupplier.Context context) { return new RecordTimestampAssigner<>(); } @PublicEvolving default WatermarkAlignmentParams getAlignmentParameters() { return WatermarkAlignmentParams.WATERMARK_ALIGNMENT_DISABLED; }
可以看到,除了
createWatermarkGenerator
外,createTimestampAssigner
和getAlignmentParameters
在WatermarkStrategy
接口中均有默认实现并返回默认值。
指定 WatermarkGenerator
的方法
WatermarkStrategy
接口提供了如下 4 个指定 WatermarkGenerator
的默认方法。因为 WatermarkStrategy
接口只有 createWatermarkGenerator
这 1 个方法没有默认实现,所以在实现上,就是用 lambda 表达式实现了 WatermarkStrategy
接口。
forMonotonousTimestamps
:使用AscendingTimestampsWatermarks
生成 watermark,适用于事件时间单调递增的场景forBoundedOutOfOrderness
:使用BoundedOutOfOrdernessWatermarks
生成 watermark,适用于事件时间虽然不单调递增,但延迟时间有限的场景forGenerator
:使用参数提供的WatermarkGeneratorSupplier
生成的 watermark 生成器noWatermarks
:不使用 watermark
源码:
flink-core/src/main/java/org/apache/flink/api/common/eventtime/WatermarkStrategy.java#L197
【Github】static <T> WatermarkStrategy<T> forMonotonousTimestamps() { return (ctx) -> new AscendingTimestampsWatermarks<>(); } static <T> WatermarkStrategy<T> forBoundedOutOfOrderness(Duration maxOutOfOrderness) { return (ctx) -> new BoundedOutOfOrdernessWatermarks<>(maxOutOfOrderness); } static <T> WatermarkStrategy<T> forGenerator(WatermarkGeneratorSupplier<T> generatorSupplier) { return generatorSupplier::createWatermarkGenerator; } static <T> WatermarkStrategy<T> noWatermarks() { return (ctx) -> new NoWatermarksGenerator<>(); }
指定 TimestampAssigner
的方法
WatermarkStrategy
接口提供了如下 3 个指定 TimestampAssigner
的默认方法。这些方法会创建一个新的 WatermarkStrategy
类,并将调用该方法的基础 WatermarkStrategy
类封装起来;在调用新类的 createWatermarkGenerator
方法时,会使用新类中重写的方法;在调用新类的其他方法时,会返回被封装的基础类的方法。
withTimestampAssigner(TimestampAssignerSupplier<T> timestampAssigner)
:使用参数指定的TimestampAssigner
的提供者类withTimestampAssigner(SerializableTimestampAssigner<T> timestampAssigner)
:使用参数指定的可序列化的TimestampAssigner
withIdleness(Duration idleTimeout)
:使用可处理空闲数据源的WatermarkStrategyWithIdleness
,其参数判定为空闲的时长
源码:
flink-core/src/main/java/org/apache/flink/api/common/eventtime/WatermarkStrategy.java#L92
【Github】default WatermarkStrategy<T> withTimestampAssigner( TimestampAssignerSupplier<T> timestampAssigner) { checkNotNull(timestampAssigner, "timestampAssigner"); return new WatermarkStrategyWithTimestampAssigner<>(this, timestampAssigner); } default WatermarkStrategy<T> withTimestampAssigner( SerializableTimestampAssigner<T> timestampAssigner) { checkNotNull(timestampAssigner, "timestampAssigner"); return new WatermarkStrategyWithTimestampAssigner<>( this, TimestampAssignerSupplier.of(timestampAssigner)); } default WatermarkStrategy<T> withIdleness(Duration idleTimeout) { checkNotNull(idleTimeout, "idleTimeout"); checkArgument( !(idleTimeout.isZero() || idleTimeout.isNegative()), "idleTimeout must be greater than zero"); return new WatermarkStrategyWithIdleness<>(this, idleTimeout); }
指定 AlignmentParameters
的方法
与指定 TimestampAssigner
的方法类似,指定 AlignmentParameters
的 2 个方法也会创建新类将基础 WatermarkStrategy
类封装起来,并重写新类的 getAlignmentParameters
方法。
withWatermarkAlignment(String watermarkGroup, Duration maxAllowedWatermarkDrift)
:指定 watermark 对齐策略,watermarkGroup
参数指定 watermark 的组名,maxAllowedWatermarkDrift
指定在暂停消费前允许超出 watermark 时间withWatermarkAlignment(String watermarkGroup, Duration maxAllowedWatermarkDrift, Duration updateInterval)
:指定 watermark 对齐策略,updateInterval
参数指定 task 上报 watermark 以及调度器制定对齐 watermark 的时间间隔
@PublicEvolving
default WatermarkStrategy<T> withWatermarkAlignment(
String watermarkGroup, Duration maxAllowedWatermarkDrift) {
return withWatermarkAlignment(
watermarkGroup,
maxAllowedWatermarkDrift,
WatermarksWithWatermarkAlignment.DEFAULT_UPDATE_INTERVAL);
}
@PublicEvolving
default WatermarkStrategy<T> withWatermarkAlignment(
String watermarkGroup, Duration maxAllowedWatermarkDrift, Duration updateInterval) {
return new WatermarksWithWatermarkAlignment<T>(
this, watermarkGroup, maxAllowedWatermarkDrift, updateInterval);
}
整体说明
通过上述设计,使 WatermarkStrategy
接口允许使用实现 WatermarkGenerator
方法的 Java Lambda 表达式实现;对于已实现的 WatermarkStrategy
匿名类,允许通过调用指定 TimestampAssigner
和 AlignmentParameters
的方法,来覆盖掉这两个方法的默认实现。