文章目录
1. 累加器的简单介绍
累加器是从用户函数和操作中,分布式地统计或者聚合信息。每个并行实例创建并更新自己的Accumulator对象, 然后合并收集器的不同并行实例。在作业结束时由系统合并。
累加器的结果可以从作业执行的结果中获得,也可以从Web运行时监视器中获得。
累加器是受Hadoop/MapReduce计数器的启发。但是要注意添加到累加器的类型可能与返回的类型不同。比如:我们添加单个对象,但是结果返回的是对象的set集合。
可以先看下Flink源码对累加器Accumulator的定义:
package org.apache.flink.api.common.accumulators;
import org.apache.flink.annotation.Public;
import java.io.Serializable;
/**
* 累加器从用户函数和操作中,分布式地统计信息或聚合。
* 每个并行实例创建并更新自己的Accumulator对象, 然后合并收集器的不同并行实例。 在作业结束时由系统合并。
* 结果可以从作业执行的结果中获得,也可以从Web运行时监视器中获得。
*
* 累加器是受Hadoop/MapReduce计数器而激发出来的。
*
* 添加到收集器的类型可能与返回的类型不同.
* 例如set类机器: 我们添加单个对象,但是结果返回的是对象的set集合
*
* @param <V> 添加到累加器的值的类型
*
* @param <R> 将向客户端报告的累加器结果的类型
*
*/
@Public
public interface Accumulator<V, R extends Serializable> extends Serializable, Cloneable {
/**
* @param value 要添加到Accumulator对象的值。
*
*/
void add(V value);
/**
* @return local 当前UDF上下文中的本地值。
*/
R getLocalValue();
/**
* 重置本地值。这只影响当前的UDF上下文。
*/
void resetLocal();
/**
* 由系统内部使用,用于在作业结束时合并收集器的收集部分。
*
* @param other 对要合并的收集器的引用。
*/
void merge(Accumulator<V, R> other);
/**
* 复制收集器。所有子类都需要正确地实现克隆,并且不能报错 {@link java.lang.CloneNotSupportedException}
*
* @return 复制的累加器。
*/
Accumulator<V, R> clone();
}
所有我们在任务中自定义的累加器必须要实现这个Accumulator接口。
2. 案例说明
本案例是要实现筛选并计数csv文件中包含空字段的行。并且使用自定义累加器计算csv文件中每列的空字段数。在此案例中,空字段是指那些最多包含空格和制表符等空白字符的字段。
输入文件是纯文本的csv文件,以分号作为字段分隔符,双引号作为字段分隔符和三列。
从这个案例中,我们可以学习到:
- 自定义累加器
- tuple数据类型
- 内联定义函数
- 命名大型元组类型
3. 代码实现
3.1. 主函数入口分析
从main主函数分析程序的逻辑比较直接点。在代码注释中,主要注释出了四步,因为这四步比较关键。后面的讲述也将围绕这四步来分开讲解。
package org.apache.flink.examples.java.relational;
import org.apache.flink.api.common.JobExecutionResult;
import org.apache.flink.api.common.accumulators.Accumulator;
import org.apache.flink.api.common.functions.RichFilterFunction;
import org.apache.flink.api.java.DataSet;
import org.apache.flink.api.java.ExecutionEnvironment;
import org.apache.flink.api.java.tuple.Tuple3;
import org.apache.flink.api.java.utils.ParameterTool;
import org.apache.flink.configuration.Configuration;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class EmptyFieldsCountAccumulator {
private static final String EMPTY_FIELD_ACCUMULATOR = "empty-fields";
public static void main(final String[