Flink 尚硅谷 总结一
Flink介绍
Fink是什么
- Apache Flink is a framework and distributed’processing engine for stateful computations over unbounded and bounded data streams.
- Apache Flink是一个框架和分布式处理引擎,用于对无界和有界数据流进行状态计算。
为什么选择Flink
-
流数据更真实地反映了我们的生活方式
-
传统的数据架构是基于有限数据集的
-
目标
- 低延时
- 高吞吐
- 结果的准确性(有序) 和良好的容错性
-
实时计算 避免最后一次性的批处理
数据处理框架历史
-
事务处理:实时性好,海量并发处理不了
-
分析处理:将数据从业务数据库复制到数仓,再进行分析和查询。能处理海量数据,实时性差
-
有状态的流式数据处理:相当于把事务处理 放在本地状态(本地内存)中,高并发的时候可以用集群。checkpoint检查点放在原处,检测集群是否挂了
-
lambda架构:用两套系统保证低时延和结果准确
-
Flink
Flink的特点
-
事件驱动(Event-driven)
-
基于流处理。一切皆由流组成,离线数据是有界的流;实时数据是一个没有界限的流。(有界流、无界流)
-
分层API
- 越顶层越抽象,表达含义越简明,使用越方便
- 越底层越具体,表达能力越丰富,使用越灵活
- 从上到下 SQL(表)-> DataStream(实时 流处理、窗口控制)、DataSet(离线) -> ProcessFunction(事件、状态、时间)
-
支持事件时间(event-time) 和处理时间(processing-time)语义
-
精确一次(exactly-once) 的状态- 致性保证
-
低延迟,每秒处理数百万个事件,毫秒级延迟
-
与众多常用存储系统的连接
-
高可用,动态扩展,实现7*24小时全天候运行
Flink vs Spark Streaming
-
延迟上区别 流处理 和 微批处理
-
数据模型
- Spark采用RDD模型,spark streaming的DStream实际上也就是一组组小批数据RDD的集合
- flink基本数据模型是数据流,以及事件(Event)序列
-
运行时架构
- spark是批计算,将DAG划分为不同的stage,一个完成后才可以计算下一个
- flink是标准的流执行模式,一个事件在一个节点处理完后可以直接发往下一个节点处理
快速上手
批处理实现WordCount
pom依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>Flink_Tutorial</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<flink.version>1.12.1</flink.version>
<scala.binary.version>2.12</scala.binary.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-java</artifactId>
<version>${flink.version}</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-scala_${scala.binary.version}</artifactId>
<version>${flink.version}</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-clients_${scala.binary.version}</artifactId>
<version>${flink.version}</version>
</dependency>
</dependencies>
</project>
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.java.DataSet;
import org.apache.flink.api.java.ExecutionEnvironment;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.util.Collector;
public class WordCount {
public static void main(String[] args) throws Exception {
// 创建执行环境
ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
// 从文件中读取数据
String inputPath = "/tmp/Flink_Tutorial/src/main/resources/hello.txt";
DataSet<String> inputDataSet = env.readTextFile(inputPath);
// 对数据集进行处理,按空格分词展开,转换成(word, 1)二元组进行统计
// 按照第一个位置(0)的word分组
// 按照第二个位置上(1)的数据求和
DataSet<Tuple2<String, Integer>> resultSet = inputDataSet.flatMap(new MyFlatMapper())
.groupBy(0)
.sum(1);
resultSet.print();
}
// 自定义类,实现FlatMapFunction接口
public static class MyFlatMapper implements FlatMapFunction<String, Tuple2<String, Integer>> {
@Override
public void flatMap(String s, Collector<Tuple2<String, Integer>> out) throws Exception {
// 按空格分词
String[] words = s.split(" ");
// 遍历所有word,包成二元组输出
for (String str : words) {
out.collect(new Tuple2<>(str, 1));
}
}
}
}
输出:
(scala,1)
(flink,1)
(world,1)
(hello,4)
(and,1)
(fine,1)
(how,1)
(spark,1)
(you,3)
(are,1)
(thank,1)
流处理实现
在2.1批处理的基础上,新建一个类进行改动。
- 批处理=>几组或所有数据到达后才处理;流处理=>有数据来就直接处理,不等数据堆叠到一定数量级
- 这里不像批处理有groupBy => 所有数据统一处理,而是用流处理的keyBy => 每一个数据都对key进行hash计算,进行类似分区的操作,来一个数据就处理一次,所有中间过程都有输出!
- 并行度:开发环境的并行度默认就是计算机的CPU逻辑核数
代码实现
package wc;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.client.program.StreamContextEnvironment;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
public class StreamWordCount {
public static void main(String[] args) throws Exception {
// 创建流处理执行环境
StreamExecutionEnvironment env = StreamContextEnvironment.getExecutionEnvironment();
// 设置并行度,默认值 = 当前计算机的CPU逻辑核数(设置成1即单线程处理)
// env.setMaxParallelism(4);
// 从文件中读取数据
String inputPath = "/tmp/Flink_Tutorial/src/main/resources/hello.txt";
DataStream<String> inputDataStream = env.readTextFile(inputPath);
// 基于数据流进行转换计算
DataStream<Tuple2<String,Integer>> resultStream = inputDataStream.flatMap(new WordCount.MyFlatMapper())
.keyBy(item->item.f0)
.sum(1);
resultStream.print();
// 执行任务
env.execute();
}
}
输出:
这里因为是流处理,所以所有中间过程都会被输出,前面的序号就是并行执行任务的线程编号。
9> (world,1)
5> (hello,1)
8> (are,1)
10> (you,1)
11> (how,1)
6> (thank,1)
9> (fine,1)
10> (you,2)
10> (you,3)
15> (and,1)
5> (hello,2)
13> (flink,1)
1> (spark,1)
5> (hello,3)
1> (scala,1)
5> (hello,4)
这里env.execute();
之前的代码,可以理解为是在定义任务,只有执行env.execute()
后,Flink才把前面的代码片段当作一个任务整体(每个线程根据这个任务操作,并行处理流数据)。
流式数据源测试
-
通过
nc -lk <port>
打开一个socket服务,用于模拟实时的流数据nc -lk 7777
-
代码修改inputStream的部分
package wc; import org.apache.flink.api.java.tuple.Tuple2; import org.apache.flink.client.program.StreamContextEnvironment; import org.apache.flink.streaming.api.datastream.DataStream; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; public class StreamWordCount { public static void main(String[] args) throws Exception { // 创建流处理执行环境 StreamExecutionEnvironment env = StreamContextEnvironment.getExecutionEnvironment(); // 设置并行度,默认值 = 当前计算机的CPU逻辑核数(设置成1即单线程处理) // env.setMaxParallelism(32); // 从文件中读取数据 // String inputPath = "/tmp/Flink_Tutorial/src/main/resources/hello.txt"; // DataStream<String> inputDataStream = env.readTextFile(inputPath); // // parameter tool ParameterTool parameterTool = ParameterTool.fromArgs(args); String host = parameterTool.get("host"); int port = parameterTool.getInt(key: "port"); // 从socket文本流读取数据 DataStream<String> inputDataStream = env.socketTextStream(host, post); // 基于数据流进行转换计算 DataStream<Tuple2<String,Integer>> resultStream = inputDataStream.flatMap(new WordCount.MyFlatMapper()) .keyBy(item->item.f0) .sum(1); resultStream.print(); // 执行任务 env.execute(); } }
-
在本地开启的socket中输入数据,观察IDEA的console输出。
本人测试后发现,同一个字符串,前面输出的编号是一样的,因为key => hashcode,同一个key的hash值固定,分配给相对应的线程处理。
Flink 部署
task slot 多线程
-
Flink 中每一个 TaskManager 都是一个JVM进程,它可能会在独立的线程上执行一个或多个 subtask
-
为了控制一个 TaskManager 能接收多少个 task, TaskManager 通过 task slot 来进行控制(一个 TaskManager 至少有一个 slot)
-
每个task slot表示TaskManager拥有资源的一个固定大小的子集。假如一个TaskManager有三个slot,那么它会将其管理的内存分成三份给各个slot(注:这里不会涉及CPU的隔离,slot仅仅用来隔离task的受管理内存)
-
可以通过调整task slot的数量去自定义subtask之间的隔离方式。如一个TaskManager一个slot时,那么每个task group运行在独立的JVM中。而当一个TaskManager多个slot时,多个subtask可以共同享有一个JVM,而在同一个JVM进程中的task将共享TCP连接和心跳消息,也可能共享数据集和数据结构,从而减少每个task的负载。
-
默认情况下,Flink 允许子任务共享 slot,即使它们是不同任务的子任务(前提是它们来自同一个job)。 这样的结果是,一个 slot 可以保存作业的整个管道。
-
Task Slot 是静态的概念,是指 TaskManager 具有的并发执行能力,可以通过参数taskmanager.numberOfTaskSlots进行配置;
-
并行度parallelism是动态概念,即TaskManager运行程序时实际使用的并发能力,可以通过参数parallelism.default进行配置。
-
# The number of task slots that each TaskManager offers. Each slot runs one parallel pipeline. taskmanager.numberOfTaskSlots: 1 # The parallelism used for programs that did not specify and other parallelism. parallelism.default: 1
注:Flink存储State用的是堆外内存,所以web UI里
JVM Heap Size
和Flink Managed MEM
是两个分开的值。
Web UI提交
启动Flink后,可以在Web UI的Submit New Job
提交jar包,然后指定Job参数。
-
Entry Class
程序的入口,指定入口类(类的全限制名)
-
Program Arguments
程序启动参数,例如
--host localhost --port 7777
-
Parallelism 设置Job并行度。
Ps:并行度优先级(从上到下优先级递减)
-
代码中算子
setParallelism()
-
ExecutionEnvironment env.setMaxParallelism()
-
设置的Job并行度
-
集群conf配置文件中的
parallelism.default
-
ps:socket等特殊的IO操作,本身不能并行处理,并行度只能是1
-
-
Savepoint Path
savepoint是通过checkpoint机制为streaming job创建的一致性快照,比如数据源offset,状态等。
(savepoint可以理解为手动备份,而checkpoint为自动备份)
-
ps:提交job要注意分配的slot总数是否足够使用,如果slot总数不够,那么job执行失败。(资源不够调度)
命令行提交job
- 查看已提交的所有job
bin/flink list
- 提交job
bin/flink run -c <入口类> -p <并行度> <jar包路径> <启动参数>
- 取消job
bin/flink cancel <Job的ID>
Yarn模式
Sesstion Cluster模式
Session-Cluster 模式需要先启动集群,然后再提交作业,接着会向 yarn 申请一块空间后,资源永远保持不变。如果资源满了,下一个作业就无法提交,只能等到 yarn 中的其中一个作业执行完成后,释放了资源,下个作业才会正常提交。所有作业共享 Dispatcher 和 ResourceManager;共享资源;适合规模小执行时间短的作业。
在 yarn 中初始化一个 flink 集群,开辟指定的资源,以后提交任务都向这里提交。这个 flink 集群会常驻在 yarn 集群中,除非手工停止。
-
启动yarn-session
./yarn-session.sh -n 2 -s 2 -jm 1024 -tm 1024 -nm test -d
其中:
-n(--container)
:TaskManager的数量。-s(--slots)
:每个TaskManager的slot数量,默认一个slot一个core,默认每个taskmanager的slot的个数为1,有时可以多一些taskmanager,做冗余。-jm
:JobManager的内存(单位MB)。-tm
:每个taskmanager的内存(单位MB)。-nm
:yarn 的appName(现在yarn的ui上的名字)。-d
:后台执行。
-
执行任务
./flink run -c com.atguigu.wc.StreamWordCount FlinkTutorial-1.0-SNAPSHOT-jar-with-dependencies.jar --host lcoalhost –port 7777
-
去 yarn 控制台查看任务状态
-
取消 yarn-session
yarn application --kill application_1577588252906_0001
Per Job Cluster 模式
一个 Job 会对应一个集群,每提交一个作业会根据自身的情况,都会单独向 yarn 申请资源,直到作业执行完成,一个作业的失败与否并不会影响下一个作业的正常提交和运行。独享 Dispatcher 和 ResourceManager,按需接受资源申请;适合规模大长时间运行的作业。
每次提交都会创建一个新的 flink 集群,任务之间互相独立,互不影响,方便管理。任务执行完成之后创建的集群也会消失。
-
不启动yarn-session,直接执行job
./flink run –m yarn-cluster -c com.atguigu.wc.StreamWordCount FlinkTutorial-1.0-SNAPSHOT-jar-with-dependencies.jar --host lcoalhost –port 7777
Flink运行框架
运行时的组件
Flink运行时架构主要包括四个不同的组件,它们会在运行流处理应用程序时协同工作:
- 作业管理器(JobManager)
- 资源管理器(ResourceManager)
- 任务管理器(TaskManager)
- 分发器(Dispatcher)
作业管理器(JobManager)
-
控制一个应用程序执行的主进程,也就是说,每个应用程序都会被一个不同的JobManager所控制执行。
-
JobManager会先接收到要执行的应用程序,这个应用程序会包括:
- 作业图(JobGraph)
- 逻辑数据流图(logical dataflow graph)
- 打包了所有的类、库和其它资源的JAR包。
-
JobManager会把JobGraph转换成一个物理层面的数据流图,这个图被叫做“执行图”(ExecutionGraph),包含了所有可以并发执行的任务。
-
JobManager会向资源管理器(ResourceManager)请求执行任务必要的资源,也就是任务管理器(TaskManager)上的插槽(slot)。一旦它获取到了足够的资源,就会将执行图分发到真正运行它们的TaskManager上。
-
在运行过程中,JobManager会负责所有需要中央协调的操作,比如说检查点(checkpoints)的协调。
资源管理器(ResourceManager)
-
主要负责管理任务管理器(TaskManager)的插槽(slot),TaskManger插槽是Flink中定义的处理资源单元。
-
Flink为不同的环境和资源管理工具提供了不同资源管理器,比如YARN、Mesos、K8s,以及standalone部署。
-
当JobManager申请插槽资源时,ResourceManager会将有空闲插槽的TaskManager分配给JobManager。如果ResourceManager没有足够的插槽来满足JobManager的请求,它还可以向资源提供平台发起会话,以提供启动TaskManager进程的容器。
-
另外,ResourceManager还负责终止空闲的TaskManager,释放计算资源。
任务管理器(TaskManager)
-
Flink中的工作进程。通常在Flink中会有多个TaskManager运行,每一个TaskManager都包含了一定数量的插槽(slots)。插槽的数量限制了TaskManager能够执行的任务数量。
-
启动之后,TaskManager会向资源管理器注册它的插槽;收到资源管理器的指令后,TaskManager就会将一个或者多个插槽提供给JobManager调用。JobManager就可以向插槽分配任务(tasks)来执行了。
-
在执行过程中,一个TaskManager可以跟其它运行同一应用程序的TaskManager交换数据。
分发器(Dispatcher)
-
可以跨作业运行,它为应用提交提供了REST接口。
-
当一个应用被提交执行时,分发器就会启动并将应用移交给一个JobManager。由于是REST接口,所以Dispatcher可以作为集群的一个HTTP接入点,这样就能够不受防火墙阻挡。Dispatcher也会启动一个Web UI,用来方便地展示和监控作业执行的信息。
-
Dispatcher在架构中可能并不是必需的,这取决于应用提交运行的方式。
任务提交流程
- 我们来看看当一个应用提交执行时,Flink的各个组件是如何交互协作的:
-
ps:上图中7.指TaskManager为JobManager提供slots,8.表示JobManager提交要在slots中执行的任务给TaskManager。
-
具体地,如果我们将Flink集群部署到YARN上,那么就会有如下的提交流程:
- Flink任务提交后,Client向HDFS上传Flink的Jar包和配置
- 之后客户端向Yarn ResourceManager提交任务,ResourceManager分配Container资源并通知对应的NodeManager启动ApplicationMaster
- ApplicationMaster启动后加载Flink的Jar包和配置构建环境,去启动JobManager,之后JobManager向Flink自身的RM进行申请资源,自身的RM向Yarn 的ResourceManager申请资源(因为是yarn模式,所有资源归yarn RM管理)启动TaskManager
- Yarn ResourceManager分配Container资源后,由ApplicationMaster通知资源所在节点的NodeManager启动TaskManager
- NodeManager加载Flink的Jar包和配置构建环境并启动TaskManager,TaskManager启动后向JobManager发送心跳包,并等待JobManager向其分配任务。
任务调度原理
-
客户端不是运行时和程序执行的一部分,但它用于准备并发送dataflow(JobGraph)给Master(JobManager),然后,客户端断开连接或者维持连接以等待接收计算结果。而Job Manager会产生一个执行图(Dataflow Graph)
-
当 Flink 集群启动后,首先会启动一个 JobManger 和一个或多个的 TaskManager。由 Client 提交任务给 JobManager,JobManager 再调度任务到各个 TaskManager 去执行,然后 TaskManager 将心跳和统计信息汇报给 JobManager。TaskManager 之间以流的形式进行数据的传输。上述三者均为独立的 JVM 进程。
-
Client 为提交 Job 的客户端,可以是运行在任何机器上(与 JobManager 环境连通即可)。提交 Job 后,Client 可以结束进程(Streaming的任务),也可以不结束并等待结果返回。
-
JobManager 主要负责调度 Job 并协调 Task 做 checkpoint,职责上很像 Storm 的 Nimbus。从 Client 处接收到 Job 和 JAR 包等资源后,会生成优化后的执行计划,并以 Task 的单元调度到各个 TaskManager 去执行。
-
TaskManager 在启动的时候就设置好了槽位数(Slot),每个 slot 能启动一个 Task,Task 为线程。从 JobManager 处接收需要部署的 Task,部署启动后,与自己的上游建立 Netty 连接,接收数据并处理。
注:如果一个Slot中启动多个线程,那么这几个线程类似CPU调度一样共用同一个slot
TaskManger与Slots与共享组
-
考虑到Slot分组,所以实际运行Job时所需的Slot总数 = 每个Slot组中的最大并行度。
-
可以在代码中通过算子的
.slotSharingGroup("组名")
指定算子所在的Slot组名,默认每一个算子的SlotGroup和上一个算子相同,而默认的SlotGroup就是"default" eg(1,1,2,1),其中第一个归为组“red”、第二个归组“blue”、第三个和第四归组“green”,那么运行所需的slot即max(1)+max(1)+max(2,1) = 1+1+2 = 4 -
Flink中每一个worker(TaskManager)都是一个JVM进程,它可能会在独立的线程上执行一个或多个subtask。为了控制一个worker能接收多少个task,worker通过task slot来进行控制(一个worker至少有一个task slot)。
-
同一个SlotGroup的算子能共享同一个slot,不同组则必须另外分配独立的Slot。
-
默认情况下,Flink允许子任务共享slot,即使它们是不同任务的子任务(前提需要来自同一个Job)。这样结果是,一个slot可以保存作业的整个管道pipeline。
- 不同任务共享同一个Slot的前提:这几个任务前后顺序不同,如上图中Source和keyBy是两个不同步骤顺序的任务,所以可以在同一个Slot执行。
- 一个slot可以保存作业的整个管道的好处:
- 如果有某个slot执行完了整个任务流程,那么其他任务就可以不用继续了,这样也省去了跨slot、跨TaskManager的通信损耗(降低了并行度)
- 同时slot能够保存整个管道,使得整个任务执行健壮性更高,因为某些slot执行出异常也能有其他slot补上。
- 有些slot分配到的子任务非CPU密集型,有些则CPU密集型,如果每个slot只完成自己的子任务,将出现某些slot太闲,某些slot过忙的现象。
- 假设拆分的多个Source子任务放到同一个Slot,那么任务不能并行执行了=>因为多个相同步骤的子任务需要抢占的具体资源相同,比如抢占某个锁,这样就不能并行。
-
Task Slot是静态的概念,是指TaskManager具有的并发执行能力,可以通过参数
taskmanager.numberOfTaskSlots
进行配置。 -
并行度parallelism是动态概念,即TaskManager运行程序时实际使用的并发能力,可以通过参数
parallelism.default
进行配置。 -
需要注意的是,这里不会涉及到CPU的隔离,slot目前仅仅用来隔离task的受管理的内存。
Slot和并行度
-
子任务就是所有的并行数之和,实际运行Job时所需的Slot总数 = 每个Slot组中的最大并行度
-
一个特定算子的 子任务(subtask)的个数被称之为其并行度(parallelism),我们可以对单独的每个算子进行设置并行度,也可以直接用env设置全局的并行度,更可以在页面中去指定并行度。
-
最后,由于并行度是实际Task Manager处理task 的能力,而一般情况下,一个 stream 的并行度,可以认为就是其所有算子中最大的并行度,则可以得出在设置Slot时,在所有设置中的最大设置的并行度大小则就是所需要设置的Slot的数量。(如果Slot分组,则需要为每组Slot并行度最大值的和)
程序与数据流
- 所有的Flink程序都是由三部分组成的: Source 、Transformation 和 Sink。
- Source 负责读取数据源,Transformation 利用各种算子进行处理加工,Sink 负责输出
- 在运行时,Flink上运行的程序会被映射成“逻辑数据流”(dataflows),它包含了这三部分
- 每一个dataflow以一个或多个sources开始以一个或多个sinks结束。dataflow类似于任意的有向无环图(DAG)
- 在大部分情况下,程序中的转换运算(transformations)跟dataflow中的算子(operator)是一一对应的关系
执行图
-
由Flink程序直接映射成的数据流图是StreamGraph,也被称为逻辑流图,因为它们表示的是计算逻辑的高级视图。为了执行一个流处理程序,Flink需要将逻辑流图转换为物理数据流图(也叫执行图),详细说明程序的执行方式。
-
Flink 中的执行图可以分成四层:StreamGraph -> JobGraph -> ExecutionGraph -> 物理执行图。
- StreamGraph:是根据用户通过Stream API 编写的代码生成的最初的图。用来表示程序的拓扑结构。
- JobGraph:StreamGraph经过优化后生成了JobGraph,提交给JobManager 的数据结构。主要的优化为,将多个符合条件的节点chain 在一起作为一个节点,这样可以减少数据在节点之间流动所需要的序列化/反序列化/传输消耗。
- ExecutionGraph:JobManager 根据JobGraph 生成ExecutionGraph。ExecutionGraph是JobGraph的并行化版本,是调度层最核心的数据结构。
- 物理执行图:JobManager 根据ExecutionGraph 对Job 进行调度后,在各个TaskManager 上部署Task 后形成的“图”,并不是一个具体的数据结构。
数据传输形式
- 一个程序中,不同的算子可能具有不同的并行度
- 算子之间传输数据的形式可以是 one-to-one (forwarding) 的模式也可以是redistributing 的模式,具体是哪一种形式,取决于算子的种类
- One-to-one:stream维护着分区以及元素的顺序(比如source和map之间)。这意味着map 算子的子任务看到的元素的个数以及顺序跟 source 算子的子任务生产的元素的个数、顺序相同。map、fliter、flatMap等算子都是one-to-one的对应关系。
- Redistributing:stream的分区会发生改变。每一个算子的子任务依据所选择的transformation发送数据到不同的目标任务。例如,keyBy 基于 hashCode 重分区、而 broadcast 和 rebalance (轮询)会随机重新分区,这些算子都会引起redistribute过程,而 redistribute 过程就类似于 Spark 中的 shuffle 过程。
任务链(OperatorChains)
-
Flink 采用了一种称为任务链的优化技术,可以在特定条件下减少本地通信的开销。为了满足任务链的要求,必须将两个或多个算子设为相同的并行度,并通过本地转发(local forward)的方式进行连接
-
相同并行度的one-to-one 操作,Flink 这样相连的算子链接在一起形成一个 task,原来的算子成为里面的 subtask
-
如果前后任务逻辑上可以是OneToOne,且并行度一致,那么就能合并在一个Slot里(并行度原本是多少就是多少,两者并行度一致)执行。
-
代码中如果
算子.disableChaining()
,能够强制当前算子的子任务不参与任务链的合并,即不和其他Slot资源合并,但是仍然可以保留“Slot共享”的特性。 -
如果
StreamExecutionEnvironment env.disableOperatorChaining()
则当前执行环境全局设置算子不参与"任务链的合并"。 -
如果
算子.startNewChain()
表示不管前面任务链合并与否,从当前算子往后重新计算任务链的合并。通常用于前面强制不要任务链合并,而当前往后又需要任务链合并的特殊场景。