我们有一个数据流,其中每个元素都是这种类型:
id: String
type: Type
amount: Integer
我们要汇总此流,并每周输出一次amount
。
当前解决方案:
示例flink管道如下所示:
stream.keyBy(type)
.window(TumblingProcessingTimeWindows.of(Time.days(7)))
.reduce(sumAmount())
.addSink(someOutput())
输入
| id | type | amount |
| 1 | CAT | 10 |
| 2 | DOG | 20 |
| 3 | CAT | 5 |
| 4 | DOG | 15 |
| 5 | DOG | 50 |
如果窗口在记录3
和4
之间结束,我们的输出将是:
| TYPE | sumAmount |
| CAT | 15 | (id 1 and id 3 added together)
| DOG | 20 | (only id 2 as been 'summed')
Id 4
和5
仍将位于flink管道中,并将在下周输出。
因此,下周我们的总产量将为:
| TYPE | sumAmount |
| CAT | 15 | (of last week)
| DOG | 20 | (of last week)
| DOG | 65 | (id 4 and id 5 added together)
新要求:
我们现在还想知道每条记录在哪个星期内被处理。换句话说,我们的新输出应为:
| TYPE | sumAmount | weekNumber |
| CAT | 15 | 1 |
| DOG | 20 | 1 |
| DOG | 65 | 2 |
但是我们还想要这样的附加输出:
| id | weekNumber |
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 2 |
| 5 | 2 |
如何处理?
flink有什么办法实现这一目标?我想像一下,我们将有一个汇总功能,可以对金额进行求和,但是还可以输出带有当前星期数的每条记录,例如,但是我在文档中找不到实现此目的的方法。
(注意:我们每周处理大约1亿条记录,因此理想情况下,我们只希望在一周内将集合保持在flink的状态,而不是所有单独的记录)
编辑:
我寻求以下安东描述的解决方案:
DataStream<Element> elements =
stream.keyBy(type)
.process(myKeyedProcessFunction());
elements.addSink(outputElements());
elements.getSideOutput(outputTag)
.addSink(outputAggregates())
KeyedProcessFunction类似于:
class MyKeyedProcessFunction extends KeyedProcessFunction<Type, Element, Element>
private ValueState<ZonedDateTime> state;
private ValueState<Integer> sum;
public void processElement(Element e, Context c, Collector<Element> out) {
if (state.value() == null) {
state.update(ZonedDateTime.now());
sum.update(0);
c.timerService().registerProcessingTimeTimer(nowPlus7Days);
}
element.addAggregationId(state.value());
sum.update(sum.value() + element.getAmount());
}
public void onTimer(long timestamp, OnTimerContext c, Collector<Element> out) {
state.update(null);
c.output(outputTag, sum.value());
}
}
答案 0 :(得分:3)
reduce方法有一个变体,它以ProcessWindowFunction作为第二个参数。您将这样使用它:
stream.keyBy(type)
.window(TumblingProcessingTimeWindows.of(Time.days(7)))
.reduce(sumAmount(), new WrapWithWeek())
.addSink(someOutput())
private static class WrapWithWeek
extends ProcessWindowFunction<Event, Tuple3<Type, Long, Long>, Type, TimeWindow> {
public void process(Type key,
Context context,
Iterable<Event> reducedEvents,
Collector<Tuple3<Type, Long, Long>> out) {
Long sum = reducedEvents.iterator().next();
out.collect(new Tuple3<Type, Long, Long>(key, context.window.getStart(), sum));
}
}
通常会向ProcessWindowFunction传递一个Iterable,该Iterable包含窗口收集的所有事件,但是如果您使用reduce或Aggregation函数预聚合窗口结果,则仅将单个值传递给Iterable。有关此文档的文档为here,但是文档中的示例当前存在一个小错误,我已在此处的示例中对其进行了修复。
但是鉴于第二个输出的新要求,我建议您放弃在Windows上执行此操作的想法,而应使用键控ProcessFunction。您将需要两个每个键值ValueState:一个按周计数,另一个用于存储总和。您将需要一个每周触发一次的计时器:触发时,它应该发出类型,总和和星期数,然后递增星期数。同时,过程元素方法将仅输出每个传入事件的ID以及星期计数器的值。