Apache flink Flink能每小时生成聚合/滚动/累积数据的快照吗?
流处理的教科书示例是一个带时间戳的字数计算程序。使用以下数据示例Apache flink Flink能每小时生成聚合/滚动/累积数据的快照吗?,apache-flink,stream-processing,Apache Flink,Stream Processing,流处理的教科书示例是一个带时间戳的字数计算程序。使用以下数据示例 mario 10:00 luigi 10:01 mario 11:00 mario 12:00 我在以下几家公司看到过单词计数程序: 总数据集 mario 3 luigi 1 一组时间窗口分区 mario 10:00-11:00 1 luigi 10:00-11:00 1 mario 11:00-12:00 1 mario 12:00-13:00 1 但是,我没有发现滚动时间窗口上的字数计算程序示例,即我希望从时间开始每小时
mario 10:00
luigi 10:01
mario 11:00
mario 12:00
我在以下几家公司看到过单词计数程序:
总数据集
mario 3
luigi 1
一组时间窗口分区
mario 10:00-11:00 1
luigi 10:00-11:00 1
mario 11:00-12:00 1
mario 12:00-13:00 1
但是,我没有发现滚动时间窗口上的字数计算程序示例,即我希望从时间开始每小时为每个字生成一个字数:
mario 10:00-11:00 1
luigi 10:00-11:00 1
mario 11:00-12:00 2
luigi 11:00-12:00 1
mario 12:00-13:00 3
luigi 12:00-13:00 1
这在ApacheFlink或任何其他流处理库中都是可能的吗?谢谢
编辑:
到目前为止,我尝试了David Anderson方法的一种变体,只改变事件时间的处理时间,因为数据是timesSamped。但它并没有像我预期的那样工作。下面是代码、示例数据、它提供的结果以及我的后续问题:
public static void main(String[] args) throws Exception {
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment()
.setParallelism(1)
.setMaxParallelism(1);
env.setStreamTimeCharacteristic(EventTime);
String fileLocation = "full file path here";
DataStreamSource<String> rawInput = env.readFile(new TextInputFormat(new Path(fileLocation)), fileLocation);
rawInput.flatMap(parse())
.assignTimestampsAndWatermarks(new AssignerWithPunctuatedWatermarks<TimestampedWord>() {
@Nullable
@Override
public Watermark checkAndGetNextWatermark(TimestampedWord lastElement, long extractedTimestamp) {
return new Watermark(extractedTimestamp - 1);
}
@Override
public long extractTimestamp(TimestampedWord element, long previousElementTimestamp) {
return element.getTimestamp();
}
})
.keyBy(TimestampedWord::getWord)
.process(new KeyedProcessFunction<String, TimestampedWord, Tuple3<String, Long, Long>>() {
private transient ValueState<Long> count;
@Override
public void open(Configuration parameters) throws Exception {
count = getRuntimeContext().getState(new ValueStateDescriptor<>("counter", Long.class));
}
@Override
public void processElement(TimestampedWord value, Context ctx, Collector<Tuple3<String, Long, Long>> out) throws Exception {
if (count.value() == null) {
count.update(0L);
}
long l = ((value.getTimestamp() / 10) + 1) * 10;
ctx.timerService().registerEventTimeTimer(l);
count.update(count.value() + 1);
}
@Override
public void onTimer(long timestamp, OnTimerContext ctx, Collector<Tuple3<String, Long, Long>> out) throws Exception {
long currentWatermark = ctx.timerService().currentWatermark();
out.collect(new Tuple3(ctx.getCurrentKey(), count.value(), currentWatermark));
}
})
.addSink(new PrintlnSink());
env.execute();
}
private static long fileCounter = 0;
private static FlatMapFunction<String, TimestampedWord> parse() {
return new FlatMapFunction<String, TimestampedWord>() {
@Override
public void flatMap(String value, Collector<TimestampedWord> out) {
out.collect(new TimestampedWord(value, fileCounter++));
}
};
}
private static class TimestampedWord {
private final String word;
private final long timestamp;
private TimestampedWord(String word, long timestamp) {
this.word = word;
this.timestamp = timestamp;
}
public String getWord() {
return word;
}
public long getTimestamp() {
return timestamp;
}
}
private static class PrintlnSink implements org.apache.flink.streaming.api.functions.sink.SinkFunction<Tuple3<String, Long, Long>> {
@Override
public void invoke(Tuple3<String, Long, Long> value, Context context) throws Exception {
System.out.println(value.getField(0) + "=" + value.getField(1) + " at " + value.getField(2));
}
}
显然出了问题。对于anna
,我在20处得到了3的计数,尽管单词anna
的第三个实例直到位置22才出现。奇怪的是,edu
只出现在最后一个快照中,即使它出现在安娜的第三个实例之前。即使没有消息到达(即应生成相同的数据),我如何触发每10个“时间单位”生成一个快照
如果有人能给我指出正确的方向,我将非常感激 是的,这不仅适用于Flink,而且很容易。您可以使用KeyedProcessFunction来实现这一点,该函数将计数器保持在keyed状态,以记录到目前为止每个字/键在输入流中出现的次数。然后使用计时器触发报告 下面是一个使用处理时间计时器的示例。它每10秒打印一份报告
public class DSExample {
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env =
StreamExecutionEnvironment.getExecutionEnvironment();
env.addSource(new SocketTextStreamFunction("localhost", 9999, "\n", -1))
.keyBy(x -> x)
.process(new KeyedProcessFunction<String, String, Tuple3<Long, String, Integer>>() {
private transient ValueState<Integer> counter;
@Override
public void open(Configuration parameters) throws Exception {
counter = getRuntimeContext().getState(new ValueStateDescriptor<>("counter", Integer.class));
}
@Override
public void processElement(String s, Context context, Collector<Tuple3<Long, String, Integer>> collector) throws Exception {
if (counter.value() == null) {
counter.update(0);
long now = context.timerService().currentProcessingTime();
context.timerService().registerProcessingTimeTimer((now + 10000) - (now % 10000));
}
counter.update(counter.value() + 1);
}
@Override
public void onTimer(long timestamp, OnTimerContext context, Collector<Tuple3<Long, String, Integer>> out) throws Exception {
long now = context.timerService().currentProcessingTime();
context.timerService().registerProcessingTimeTimer((now + 10000) - (now % 10000));
out.collect(new Tuple3(now, context.getCurrentKey(), counter.value()));
}
})
.print();
env.execute();
}
}
通过这些更改,我得到了以下结果:
mario=4 at 10
luigi=1 at 10
fred=1 at 10
bob=2 at 10
vilma=1 at 10
dan=1 at 10
vilma=1 at 20
luigi=1 at 20
dylan=2 at 20
carl=1 at 20
bambam=1 at 20
mario=6 at 20
summer=1 at 20
anna=2 at 20
bob=2 at 20
fred=2 at 20
dan=1 at 20
fred=2 at 9223372036854775807
dan=1 at 9223372036854775807
carl=1 at 9223372036854775807
dylan=2 at 9223372036854775807
vilma=1 at 9223372036854775807
edu=1 at 9223372036854775807
anna=7 at 9223372036854775807
summer=1 at 9223372036854775807
bambam=1 at 9223372036854775807
luigi=1 at 9223372036854775807
bob=2 at 9223372036854775807
mario=6 at 9223372036854775807
现在,如果您需要实际处理无序事件,这将变得相当复杂。有必要让水印滞后于时间戳一个真实的量,以反映流中存在的实际无序量,这将需要能够处理一次打开多个窗口。任何给定的事件/字可能不属于下一个将关闭的窗口,因此不应增加其计数器。例如,您可以将这些“早期”事件缓冲在另一个状态(例如ListState)中,或者以某种方式维护多个计数器(可能在MapState中)。此外,有些事件可能会延迟,从而使早期报告无效,您需要定义一些处理策略。Hi@David,非常感谢您的回复。我尝试了一种类似于您的方法,只使用事件时间作为我的数据的时间戳(我编辑了我的原始帖子)。我的要求是每10个时间单位生成一个单词计数数据的快照。快照必须精确到所述时间(即对t=10,20,30…处的快照感兴趣,但对t=11处的快照不感兴趣)。如果一段时间后没有消息到达,我仍然需要在要求的时间生成快照,但数据不会更改。消息到达的延迟最多为2个时间单位。非常感谢您的帮助!顺便说一句,没有必要将并行度设置为1——如果让它并行运行,您将获得准确的结果。给定密钥的所有事件都将被串行处理。我只将parallelism设置为1以简化事情,我的实际应用程序将使用kafka,因此事件将被时间戳和排序。对了,回答得很好,谢谢。从文档中我了解到,t的水印意味着所有新消息的时间必须严格大于t,如果允许两个事件具有相同的时间戳,这会起作用吗?最后一个问题是,假设应用程序时钟的最大延迟为2个时间单位,那么即使没有新消息到达,我如何能够以10个时间单位的间隔生成快照?
@Override
public void processElement(TimestampedWord value, Context ctx, Collector<Tuple3<String, Long, Long>> out) throws Exception {
if (count.value() == null) {
count.update(0L);
setTimer(ctx.timerService(), value.getTimestamp());
}
count.update(count.value() + 1);
}
@Override
public void onTimer(long timestamp, OnTimerContext ctx, Collector<Tuple3<String, Long, Long>> out) throws Exception {
long currentWatermark = ctx.timerService().currentWatermark();
out.collect(new Tuple3(ctx.getCurrentKey(), count.value(), currentWatermark));
if (currentWatermark < Long.MAX_VALUE) {
setTimer(ctx.timerService(), currentWatermark);
}
}
private void setTimer(TimerService service, long t) {
service.registerEventTimeTimer(((t / 10) + 1) * 10);
}
mario=4 at 10
luigi=1 at 10
fred=1 at 10
bob=2 at 10
vilma=1 at 10
dan=1 at 10
vilma=1 at 20
luigi=1 at 20
dylan=2 at 20
carl=1 at 20
bambam=1 at 20
mario=6 at 20
summer=1 at 20
anna=2 at 20
bob=2 at 20
fred=2 at 20
dan=1 at 20
fred=2 at 9223372036854775807
dan=1 at 9223372036854775807
carl=1 at 9223372036854775807
dylan=2 at 9223372036854775807
vilma=1 at 9223372036854775807
edu=1 at 9223372036854775807
anna=7 at 9223372036854775807
summer=1 at 9223372036854775807
bambam=1 at 9223372036854775807
luigi=1 at 9223372036854775807
bob=2 at 9223372036854775807
mario=6 at 9223372036854775807