assignTimestampsAndWatermarks
在keyBy
之前起作用:
DataStream<Trip> trips =
env.addSource(consumer).assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor<Trip>(Time.days(1)) {
@Override
public long extractTimestamp(Trip trip) {
return trip.endTime.getTime();
}
});
KeyedStream<Trip, Long> userTrips = trips.keyBy(trip -> trip.userId);
DataStream<FeaturizedTrip> featurizedUserTrips = userTrips.process(new Featurization());
AllWindowedStream<FeaturizedTrip, TimeWindow> windowedUserTrips =
featurizedUserTrips.timeWindowAll(Time.days(7),
Time.days(1));
但不在keyBy
和process
之后:
DataStream<Trip> trips = env.addSource(consumer);
KeyedStream<Trip, Long> userTrips = trips.keyBy(trip -> trip.userId);
DataStream<FeaturizedTrip> featurizedUserTrips =
userTrips.process(new Featurization()).assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor<FeaturizedTrip>(Time.days(1)) {
@Override
public long extractTimestamp(FeaturizedTrip trip) {
return trip.endTime.getTime();
}
});
AllWindowedStream<FeaturizedTrip, TimeWindow> windowedUserTrips =
featurizedUserTrips.timeWindowAll(Time.days(7),
Time.days(1));
Windows永远不会触发。
是错误还是预期的行为?如果后者在哪里记录?
答案 0 :(得分:0)
我有一个类似的问题。就我而言,我有一个读取文件的源,每个实例读取一个单独的文件。在将一个文件发送到数据流的最简单测试中,只有一个源实例正在生成事件,而其他实例则处于空闲状态。因此,在应用水印分配器之后,当我使用AssignerWithPeriodicWatermarks时,只有一个水印分配器正在发送新的水印,而其他水印则没有,或者他们正在发送Long.MIN_VALUE
。
应用keyBy()
运算符后,空闲的事件流与活动的事件流混合在一起。由于在任何运算符处计算水印的规则都是从每个传入流中获取可用的最小值,因此链中下一个运算符process()
的每个实例都停留在水印Long.MIN_VALUE
上。因此,看起来水印在应用keyBy()
之后永远不会进行。
我的解决方案是在分配水印之前先对事件进行洗牌。在水印分配者运算符上,规则是相反的-始终获取并重新发送计算出的最高水印。这样,即使某些源实例处于空闲状态,我们也可以保证水印分配器的所有实例都在发送水印:
stream.rebalance().assignTimestampsAndWatermarks(...);
这里还有一个陷阱,您必须避免。如果您决定在不收集数据的情况下将源实例声明为空闲:
context.markAsTemporarilyIdle();
您将发现水印再次丢失,即使上述带有rebalance()
的破解也已到位。这是因为markAsTemporarilyIdle()
在代码的这一部分(来自Flink 1.7)将相应的流设置为空闲:
org.apache.flink.streaming.runtime.io.RecordWriterOutput.emitWatermark()
水印停止,因为条件streamStatusProvider.getStreamStatus().isActive()
为假。因此,得出的结论是在所述情况下不使用context.markAsTemporarilyIdle()
。