apache beam 入门之数据聚合
原创
©著作权归作者所有:来自51CTO博客作者breakDawn的原创作品,请联系作者获取转载授权,否则将追究法律责任
聚合元素(多个元素合并成1个)操作(类似于reduce操作)
如果要将数据集里的所有元素聚合成1个元素,在beam里称为combine操作。
假设现在我们有1个PCollection数据集 pInt
则我们以计算整数求和的方式,展示3种聚合方式:
用beam提供的sdk
PCollection<Integer> pSum = pInt.apply(Sum.integersGlobally());
用Combine.globally(SerializableFunction类)
PCollection<Integer> pSum = pInt.apply(Combine.globally(new IntergerSum());
IntegerSum定义如下:
//实现SerializableFunction<A,B>接口, A是管道里元素的迭代器类型, B是输出结果类型
public static class IntergerSum implements SerializableFunction<Iterable<Integer>, Integer> {
@Override
public Integer apply(Iterable<Integer> input) {
Integer sum = new Integer(0);
for (Integer item : input) {
sum += item;
}
return sum;
}
}
自定义Combine.CombineFn
这个方法较复杂,要实现4个接口,但是自由度也会高很多
可以用中间累加器做特殊的聚合操作,并最后再切换回需要的输出
PCollection<Integer> pSum = pInt.apply(Combine.globally(new IntSumFn());
/**
* 继承自Combine.CombineFn<A,B,C>
* A输入管道的元素, B中间累加器的类型, C输出结果类型
* 步骤:创建累加器、各机器管道元素合到累加器中、各管道累加器合并、处理最终结果
*/
class IntSumFn extends Combine.CombineFn<Integer, Integer, Integer> {
// 中间累加器可以自己定义
// 中间累加器初始化
@Override
public Integer createAccumulator(){ return 0;}
//单管道中的累加器操作
@Override
public Integer addInput(Integer accum, Integer input){
accum += input;
return accum;
}
//合并多个分布式机器的累加器方法
//最终返回1个累加器
@Override
public Integer mergeAccumulators(Iterable<Integer> accums){
Integer merged = createAccumulator();
for (Integer accum: accums){
merged += accum;
}
return merged;
}
//如何将累加器转化为你需要的输出结果
//这里可以对最后聚合的输出结果做特殊处理
@Override
public Integer extractOutput(Integer accum){
return accum;
}
}
利用CombineFn的中间累加器,可以灵活地实现各种聚合,我们换1个更有用的例子: 我希望将所有字符串元素合并成1个字符串,并转成自己需要的1个输出实体OutputEntity
这个需求中,如果要叠加字符串,则肯定不能直接让String相加,因为这比较消耗性能,正确的姿势是用StringBuilder做中间累加器。
class MergeStringToOutputEntity extends Combine.CombineFn<String, StringBuilder, OutputEntity> {
@Override
public StringBuilder createAccumulator() {
return new StringBuilder();
}
@Override
public StringBuilder addInput(StringBuilder mutableAccumulator, String input) {
return mutableAccumulator.append(input);
}
@Override
public StringBuilder mergeAccumulators(Iterable<StringBuilder> accumulators) {
StringBuilder mergeAccum = createAccumulator();
for(StringBuilder stringBuilder : accumulators) {
mergeAccum.append(stringBuilder);
}
return mergeAccum;
}
@Override
public OutputEntity extractOutput(StringBuilder accumulator) {
return new OutputEntity(accumulator);
}
}
不过也要注意一点,中间累加器切忌过大, 即使你控制中间累加器最多为10M,不会超出JVM内存,但也会出现合并过程及其缓慢的情况。这是beam的combine的实现决定的,后续会提到。