flink的整个数据处理流程是由一个个operator组成的,数据从源头开始传递给一个个operator进行链式处理,每一个处理逻辑就是一个operator,一个operator包含一个输入、一个处理逻辑、一个输出,operator是在TaskManager的slot中执行的,一个slot就是一个线程,一个operator只能在一个slot中执行,一个slot中可以运行多个operator(同一个job任务),flink会进行优化将多个operator放在一个slot中运行,它能减少线程之间的切换,减少消息的序列化/反序列化,减少数据在缓冲区的交换,减少了延迟的同时提高整体的吞吐量。根据我们之前给出的示例,我们来看看都有哪些operator,
source operator
DataStreamSource<String> text = env.socketTextStream("localhost", port, "\n")
transform operator
// 计算数据
DataStream<WordWithCount> windowCount = text.flatMap(new FlatMapFunction<String, WordWithCount>() {
public void flatMap(String value, Collector<WordWithCount> out) throws Exception {
String[] splits = value.split(" ");
for (String word : splits) {
out.collect(new WordWithCount(word, 1L));
}
}
})// 打平操作,把每行的单词转为<word,count>类型的数据
.keyBy("word")// 针对相同的word数据进行分组
.timeWindow(Time.seconds(8), Time.seconds(1))// 指定计算数据的窗口大小和滑动窗口大小
.sum("count");
sink operator
// 把数据打印到控制台
windowCount.print();
flink的执行流程总是以source operator开头,sink operator结尾,中间夹杂一些transform operator。
下面是运行逻辑图:
这个示例中flink将source、flatmap、window、sum、sink合并成一个chain放在同一个slot中运行(如果这里的并行度不一样,那么就不会放在一个chain中,具体例子可以看下面的参考文档),那么什么条件下slot会组合成一个chain呢?
1、上下游的并行度一致
2、下游节点的入度为1 (也就是说下游节点没有来自其他节点的输入)
3、上下游节点都在同一个 slot group 中(下面会解释 slot group)
4、下游节点的 chain 策略为 ALWAYS(可以与上下游链接,map、flatmap、filter等默认是ALWAYS)
5、上游节点的 chain 策略为 ALWAYS 或 HEAD(只能与下游链接,不能与上游链接,Source默认是HEAD)
6、两个节点间数据分区方式是 forward(一对一,没有shuffle)
7、用户没有禁用 chain
多个operator可以组成一个chain,可以作为一个新的operator,不同的operator可以运行在同一个slot中。