Flink 运行模式
本地运行模式
本地运行模式即为 local 运行模式,启动一个单机实例进行运行。该模式一般是运行简单的程序、或者调试、测试等使用,在机器为单机的情况下也会经常使用。
local 模式对应使用 API:StreamExecutionEnvironment.getExecutionEnvironment()
获取到的是 LocalStreamEnvironment
对象,这个对象在下面会说明。
集群运行模式
集群运行模式即为远程运行模式,将 Job 通过 Client 提交到集群上面,并运行。
该模式支持的集群包括:
- Standalone 模式:由 Flink 自己创建一个集群并自己管理,用的比较少;
- Yarn 模式:将任务提交到 Yarn 集群,依赖 Yarn 的资源管理来运行,这种方式用的比较多;
- K8S 模式:提交到 K8S 集群运行,这种方式使用的也比较多;
- Mesos 模式:提交到 Mesos 集群运行。
我们重点介绍 Yarn 模式,目前在大数据开发框架中,由于一般会和 Hadoop 套件一起开发,所以一般自带 Yarn 集群,通过 Yarn 来管理资源和任务。
在 Yarn 模式下运行,Flink Client 需要将任务提交到 Yarn 集群运行,提交到 Yarn 也包括三种模式:
- Session 模式:需要先启动一个 Flink 集群,然后向该集群提交作业,集群会常驻在 Yarn 中,直到手动停止,适合需要频繁提交多个小作业的场景,该方式的 main 方法在 Client 端执行,执行图在 Client 端生成;
- Per-Job 模式:每个作业都会单独向 Yarn 申请资源,启动一个新的 Flink 集群来运行作业。作业完成后,集群会被关闭。适合运行大作业或需要资源隔离的场景,该方式的 main 方法在 Client 端执行,执行图在 Client 端生成;
- Application 模式:在 Yarn 上启动集群,应用程序执行结束后,Flink 集群会立即关闭。该模式允许在单个应用程序中提交多个作业,该方式的 main 方法在 Jobmanager 上执行,执行图在 Jobmanager 生成;
三种模式的提交命令也不同:
- Session 模式:
./bin/flink run -m <jobmanager_address> -d <jar_path>
,首先需要通过./bin/start-cluster.sh
等命令启动Flink会话集群,然后再提交作业; - Per-Job 模式:
./bin/flink run -t yarn-per-job -D<property>=<value> ... <jar_path>
或新版本中使用./bin/flink run -m yarn-cluster -y<property>=<value> ... <jar_path>
提交任务,参数设置为 flink 启动参数等; - Application 模式:
./bin/flink run-application -t yarn-application -D<property>=<value> ... <jar_path>
,在 Flink 1.11+ 版本支持,会在 Jobmanager 中启动 jvm 来执行 main 方法。
特别说明,Per-Job 和 Application 模式的选择:
Per-Job 为每个作业启动一个集群,资源隔离性好,但是比较重。Application 模式的提交较为轻量级,速度也比较快,适合轻量级的任务提交。并且由于其支持一个 main 方法中有多个 Job,所以适合多个 Job 之间有依赖关系的任务。
Flink 运行时的 Environment
Flink 运行时 Environment 主要用于定义运行时的一些行为,比如并行度、数据源、任务的实际开始运行入口等等。
包括两大类:ExecutionEnvironment 和 StreamExecutionEnvironment。
其中,ExecutionEnvironment 主要是用于批处理,StreamExecutionEnvironment 主要是用于流式处理,二者之间没有继承关系。
它们各自有各自的子类,如下:
ExecutionEnvironment
+---- LocalEnvironment:本地提交作业执行,即在本地 JVM 中运行作业
+---- RemoteEnvironment:将作业提交到远程执行,即将作业提交到远程比如 yarn 上执行
+---- ...
StreamExecutionEnvironment
+---- LocalStreamEnvironment:本地执行流式处理任务,在本地 JVM 中运行
+---- RemoteStreamEnvironment:将任务提交到远程集群上云霄,比如 yarn
+---- StreamContextEnvironment:比较特殊,在上下文中模拟运行使用,比如在单测、CLI 中提交
+---- StreamPlanEnvironment:特殊,用于生成执行计划,在 Flink UI 中展示
批处理任务 ExecutionEnvironment
找到类 org.apache.flink.api.java.ExecutionEnvironment
,其中包括了定义数据源、设置并行度、设置重启策略等方法。
其中,数据源定义支持从文件读取、从集合读取等等方式读取。
在 ExecutionEnvironment 中定义了启动方法,表示启动任务。
还有一些针对任务的方法包括获取运行计划(运行图)、获取 jobName 等等操作,所有针对环境、资源配置等均可在 ExecutionEnvironment 中进行指定和配置。
ExecutionEnvironment 中运行的数据源读取后是 DataSet 类型,表示批量数据源。 在实际使用过程中,ExecutionEnvironment 使用的比较少,更多的是使用 StreamExecutionEnvironment。
一个典型的批处理例子:
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.java.ExecutionEnvironment;
import org.apache.flink.api.java.operators.DataSource;
import org.apache.flink.api.java.operators.FlatMapOperator;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.util.Collector;
/**
* 批处理
*/
@Slf4j
public class WordCountMain {
public static void main(String[] args) throws Exception {
// 1.创建执行环境,批处理执行环境
ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
// 2.从文件读取数据,继承自 DateSet,不是 DataStream
DataSource<String> dataSource = env.readTextFile("/tmp/data.txt");
// 3.提取单词,转换为二元组
FlatMapOperator<String, Tuple2<String, Long>> pairsOperator = dataSource.flatMap(
new FlatMapFunction<String, Tuple2<String, Long>>() {
@Override
public void flatMap(String line, Collector<Tuple2<String, Long>> collector) throws Exception {
String[] strs = line.split("\\s+");
for (String s : strs) {
if (StringUtils.isNotBlank(s)) {
collector.collect(Tuple2.of(s, 1L));
}
}
}
});
pairsOperator.groupBy(0) // 按照 word 分组
.sum(1) // 按照 Tuple2[1] 进行聚合
.print(); // sink
}
}
流处理任务 StreamExecutionEnvironment
与 ExecutionEnvironment 不同,StreamExecutionEnvironment 的数据源读取进来以后是 DataStream 类型,表示流式数据。
StreamExecutionEnvironment 中包括了设置 Job 监听器、设置并行度、运行模式(Batch、Streaming)、是否禁用算子链、CheckPoint 配置、State 存储配置、SavePoint 配置等。
同时在 StreamExecutionEnvironment 中也包括了从文件、集合、网络端口等读取数据的功能,这一点与 ExecutionEnvironment 类似。也支持自定义 DateSource。
在实际应用过程中,一般使用 StreamExecutionEnvironment 来完成数据处理任务,批处理方式基本不使用。StreamExecutionEnvironment 的定义在 flink-streaming-java 包中。
一段典型的使用流处理的代码如下:
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.util.Collector;
/**
* 无界流
*/
@Slf4j
public class StreamWordCountMain {
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
// env.disableOperatorChaining();
// DataStreamSource<String> dataSource = env.socketTextStream("127.0.0.1", 8899); // 命令:nc -lk 8899
DataStreamSource<String> dataSource = env.addSource(new WordDataSource());
SingleOutputStreamOperator<Tuple2<String, Long>> flatMap =
dataSource.flatMap(new FlatMapFunction<String, Tuple2<String, Long>>() {
@Override
public void flatMap(String line, Collector<Tuple2<String, Long>> collector) throws Exception {
String[] strs = line.split("\\s+");
for (String s : strs) {
if (StringUtils.isNotBlank(s)) {
collector.collect(Tuple2.of(s, 1L));
}
}
}
});
flatMap.keyBy(k -> k.f0)
.sum(1)
.print();
env.execute();
}
}
一个自定义的数据源比如:
import lombok.extern.slf4j.Slf4j;
import org.apache.flink.streaming.api.functions.source.SourceFunction;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@Slf4j
public class WordDataSource implements SourceFunction<String> {
private final Random random = new Random(System.currentTimeMillis());
private boolean isCanceled = false;
@Override
public void run(SourceContext<String> ctx) throws Exception {
List<String> data = new ArrayList<>();
BufferedReader br =
new BufferedReader(new InputStreamReader(this.getClass().getClassLoader().getResourceAsStream("data.txt")));
// new FileReader(new File("resources/data.txt"))
br.lines().forEach(line -> {
String[] seps = line.split("\\s+");
for (String sep : seps) {
sep = sep.trim();
if (sep.length() > 0) data.add(sep);
}
});
br.close();
while (!isCanceled) {
Thread.sleep(100L);
ctx.collect(data.get(random.nextInt(data.size())));
}
}
@Override
public void cancel() {
log.info("data source canceled.");
isCanceled = true;
}
}
需要注意,StreamExecutionEnvironment
是我们在编写 flink 程序的时候使用的类,而不是 flink 程序运行时候的类,在 flink 的 TaskManager 运行的时候,StreamExecutionEnvironment
会被转换为 RuntimeContext
对象,转换是通过 RuntimeEnvironment
类来转换的,RuntimeContext
中除了 StreamExecutionEnvironment
中的信息以外,还包括当前 Task 的一些信息。