Flink的AggregateFunction是一个基于中间计算结果状态进行增量计算的函数,可以用在多种场景的实时计算中,而且采用迭代方式,运行效率比较高。
一、接口
我们查看AggregateFunction接口可以看到它有三个参数
输入类型(IN)
累加器类型(ACC)
输出类型(OUT)
@PublicEvolving
public interface AggregateFunction<IN, ACC, OUT> extends Function, Serializable {
//创建一个新的累加器,启动一个新的聚合,负责迭代状态的初始化
ACC createAccumulator();
//对于数据的每条数据,和迭代数据的聚合的具体实现
ACC add(IN var1, ACC var2);
//从累加器获取聚合的结果
OUT getResult(ACC var1);
//合并两个累加器,返回一个具有合并状态的累加器(sessionwindow可用)
ACC merge(ACC var1, ACC var2);
}
那么我们什么时候可以使用AggregateFunction呢?
二、场景
问题:有一天小白想统计一下每5次各科成绩总和以及平均值,有了很多数据,你能帮助小白设计下如何方便的统计么?
统计的科目有:“语文”, “数学”, “英语”, “物理”, "化学"
我们先定义一个科目的数组SUBJECT
private static final String[] SUBJECT = { "语文", "数学", "英语", "物理", "化学" };
然后通过数据流获取相关数据(本例随机生成成绩)
数据源结构为Tuple3<String, String, Integer>
分别表示 姓名,科目,成绩
// 获取执行环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
env.setParallelism(1);
DataStream<Tuple3<String, String, Integer>> source = env.addSource(new SourceFunction<Tuple3<String, String, Integer>>() {
private volatile boolean isRunning = true;
private final Random random = new Random();
@Override
public void run(SourceContext<Tuple3<String, String, Integer>> ctx) throws Exception {
while (isRunning) {
TimeUnit.SECONDS.sleep(1);
ctx.collect(Tuple3.of("小白", SUBJECT[random.nextInt(SUBJECT.length)], (int)(Math.random() * 50) + 50));
}
}
@Override
public void cancel() {
isRunning = false;
}
});
获取数据流之后,执行数据的聚合,数据格式为Tuple4<String, String, Integer, Double>
分别表示 姓名,科目,成绩,平均成绩
DataStream<Tuple4<String, String, Integer, Double>> score = source
.keyBy(1)
.countWindow(5)
.aggregate(new AverageAggrate());
score.print();
那么 AverageAggrate()的方法如何实现呢?
三、实现
首先我们建立AverageAggrate 继承接口AggregateFunction,接口的类型如下
输入类型(IN)
Tuple3<String, String, Integer> 表示 姓名,科目,分数
累加器类型(ACC)
Tuple4<String, String, Integer, Integer> 表示 姓名,科目,总分数,统计次数
输出类型(OUT)
Tuple4<String, String, Integer, Double> 表示 姓名,科目,总分数,每个统计
public static class AverageAggrate implements AggregateFunction<Tuple3<String, String, Integer>, Tuple4<String, String, Integer, Integer>, Tuple4<String, String, Integer, Double>> {
@Override
public Tuple4<String, String, Integer, Integer> createAccumulator() {
return new Tuple4<>("", "", 0, 0);
}
@Override
public Tuple4<String, String, Integer, Integer> add(Tuple3<String, String, Integer> value, Tuple4<String, String, Integer, Integer> acc) {
return new Tuple4<>(value.f0, value.f1, acc.f2 + value.f2, acc.f3 + 1);
}
@Override
public Tuple4<String, String, Integer, Double> getResult(Tuple4<String, String, Integer, Integer> acc) {
return Tuple4.of(acc.f0, acc.f1, acc.f2, (double)acc.f2 / acc.f3);
}
@Override
public Tuple4<String, String, Integer, Integer> merge(Tuple4<String, String, Integer, Integer> acc1, Tuple4<String, String, Integer, Integer> acc2) {
return null;
}
}
运行结果:
小白每个科目 每5次的平均结果和总分数就统计完成啦