写在前面
本节主要介绍Flink中常用的算子,后续随着深入学习过程将会不断更新。整体上来说,Flink的算子可以从DataStream/DataSet、Sink/Source/Transformation/共享变量、keyBy/groupBy/noKey、RichFunction/ParallelFunciton/Function等角度划分,实际使用过程中也是需要考虑以上这些点。
1.Source算子
1.1已经实现的Source
- 基于文件的:readTextFile(path),读取文本文件,文件遵循TextInputFormat 读取规则,逐行读取并返回。
- 基于socket:socketTextStream,从socker中读取数据,元素可以通过一个分隔符切开。
- 基于集合:fromCollection(Collection),通过java 的collection集合创建一个数据流,集合中的所有元素必须是相同类型的。
- 扩展数据源:addSource, 可以实现读取第三方数据源的数据,系统内置提供了一批connectors,连接器会提供对应的source支持。常用的包括:DataStream Connectors
1.2自定义Source
单并行度数据源:SourceFunction;多并行度数据源:ParallelSourceFunction。
此外,一般带RichXXXFunction里面可以实现更多的方法(包括初始化…等操作)。
ParallelSourceFunction举例说明
每秒产生一个number
public class ExampleSource implements ParallelSourceFunction<Long> {
private long number = 1L;
private boolean isRunning = true;
@Override
public void run(SourceContext<Long> ctx) throws Exception {
while (isRunning){
ctx.collect(number);
number++;
Thread.sleep(1000);
}
}
@Override
public void cancel() {
isRunning = false;
}
}
2.Transformation算子
2.1常见算子概述
注意这些算子本身底层实现存在相互调用过程,这里的大部分算子和Spark中都是类似的,平时使用过程中注意灵活运用。
- map、flatMap、mapPartition
map:输入一个元素,然后返回一个元素,中间可以做一些清洗转换等操作;
flatMap:对集合中每个元素进行操作然后再扁平化。
mapPartition:类似map,一次处理一个分区的数据【如果在进行map处理的时候需要获取第三方资源链接,建议使用MapPartition,但是注意OOM问题】 - filter
filter:过滤函数,对传入的数据进行判断,符合条件的数据会被留下。 - reduce、aggregate
reduce:每流入一个新元素,新元素与中间数据两两合一,生成新的中间数据。
aggregate:先缓存所有元素,等到触发条件后对窗口内的全量元素执行计算。 - join、cross、union、connect
union:合并多个流,新的流会包含所有流中的数据,但是union是一个限制,就是所有合并的流类型必须是一致的。
connect:和union类似,但是只能连接两个流,两个流的数据类型可以不同,会对两个流中的数据应用不同的处理方法。
cross:获取两个数据集的笛卡尔积。
join:内连接。
2.2自定义算子
和自己实现Source算子类似,这里给出一个简单的案例
public class FlatMapFunctionDemo implements FlatMapFunction<String, Tuple2<String, Integer>> {
@Override
public void flatMap(String line, Collector<Tuple2<String, Integer>> out) throws Exception {
String[] fields = line.split(",");
for (String word : fields) {
out.collect(new Tuple2<String, Integer>(word, 1));
}
}
}
3.Sink算子
3.1常见的Sink算子
- print() / printToErr():打印每个元素的toString()方法的值到标准输出或者标准错误输出流中
- writeAsText()、writeAsCSV():写入文本或者CSV中
- 扩展数据源:见Source算子
3.2自定义Sink算子
略,参考Source部分的定义,回头遇到合适的再补充。
4.共享变量
4.1广播变量
把一个dataset 数据集广播出去,然后不同的task在节点上都能够获取到,这个数据在每个节点上只会存在一份。如果不使用broadcast,则在每个节点中的每个task中都需要拷贝一份dataset数据集,比较浪费内存(也就是一个节点中可能会存在多份dataset数据)。
注意点
- 广播出去的变量存在于每个节点的内存中,所以这个数据集不能太大。因为广播出去的数据,会常驻内存,除非程序执行结束。
- 广播变量在初始化广播出去以后不支持修改,这样才能保证每个节点的数据都是一致的。
使用过程
1、初始化数据
DataSet<Integer> toBroadcast = env.fromElements(1, 2, 3)
2、广播数据
myDataSet.withBroadcastSet(toBroadcast, "broadCastMapName");
3、获取数据
Collection<Integer> broadcastSet = getRuntimeContext().getBroadcastVariable("broadcastSetName");
4.2累加器
Accumulator即累加器,与Mapreduce counter的应用场景差不多,都能很好地观察task在运行期间的数据变化,可以在Flink job任务中的算子函数中操作累加器,但是只能在任务执行结束之后才能获得累加器的最终结果。
Counter是一个具体的累加器(Accumulator)实现,具体包括IntCounter, LongCounter 和 DoubleCounter
使用过程
- 创建累加器
private IntCounter numLines = new IntCounter();
- 注册累加器
getRuntimeContext().addAccumulator("num-lines", this.numLines);
- 使用累加器
this.numLines.add(1);
- 获取累加器的结果
myJobExecutionResult.getAccumulatorResult("num-lines")