Flink的基本学习
一、概述
ApacheFlink是一个框架和分布式处理引擎,用于对无界和有界数据流进行状态计算。
1.1 快速上手
1.哈哈先从Idea上手,先去Idea中创建一个maven项目,添加maven依赖如下:
<dependencies> <!-- 引入 Flink 相关依赖--> <dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-java</artifactId> <version>1.13.0</version> </dependency> <dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-streaming-java_2.12</artifactId> <version>1.13.0</version> </dependency> <dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-clients_2.12</artifactId> <version>1.13.0</version> </dependency> <!-- 引入日志管理相关依赖--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.30</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.30</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-to-slf4j</artifactId> <version>2.14.0</version> </dependency> </dependencies>
2.在目录 src/main/resources 下添加文件:log4j.properties,内容配置如下:
log4j.rootLogger=error, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
配置好后就可以编写代码了。
二、Flink部署
2.1 集群配置
1.本地配置。首先打开hadoop102虚拟机,进入到/opt/software/
目录下将flink-1.13.0-bin-scala_2.12.tgz
压缩包上传到虚拟机中,然后输入命令:tar -zxvf flink-1.13.0-bin-scala_2.12.tgz
解压缩。解压完成后可以进入flink里面,输入命令:bin/start-cluster.sh
,启动集群,注意这只是本地模式下的启动,启动成功后可以访问http://hadoop102:8081此webUI页面,关闭集群可以执行命令:bin/stop-cluster.sh
。
2.下面开始集群配置。将hadoop103、104同样的执行上述操作,即将flink下载到虚拟机中,并解压。然后返回到hadoop102虚拟机中进入到flink文件夹中的conf下,输入命令:vi flink-conf.yaml
,进入到该文件后,找到jobmanager.rpc.address: localhost
参数,将其修改为jobmanager.rpc.address: hadoop102
,然后同样的在conf目录下,输入命令:vi masters
,将内容改为hadoop102:8081
,完成后继续在conf目录下,输入命令:vi workers
来配置TaskManager节点,将内容修改为:
hadoop103 hadoop104
在hadoop102上配置好后,退出conf目录,输入命令:xsync conf
将配置文件分发到其它节点上,配置完成后即可启动集群。在hadoop102虚拟机上flink目录下,输入命令bin/start-cluster.sh
,查看是否启动成功,成功后可查看webUI界面。
2.2 部署模式
1.会话模式
先启动一个集群,保持一个会话,在这个会话中通过客户端提交作业。会话模式比较适合于单个规模小、执行时间短的大量作业。
2.单作业模式
会话模式因为资源共享会导致很多问题,所以为了更好地隔离资源,我们可以考虑为每个提交的作业启动一个集群,这就是所谓的单作业(Per-Job)模式(就是严格的一对一,集群只为这个作业而生),单作业模式在生产环境运行更加稳定,所以是实际应用的首选模式。
3.应用模式
该模式不需要客户端,直接把应用提交到 JobManger 上运行。
总结:*在会话模式下,集群的生命周期独立于集群上运行的任何作业的生命周期,并且提交的所有作业共享资源。而单作业模式为每个提交的作业创建一个集群,带来了更好的资源隔离,这时集群的生命周期与作业的生命周期绑定。最后,应用模式为每个应用程序创建一个会话集群,在 JobManager 上直接调用应用程序的 main()方法。 *
2.3 独立模式
独立模式(Standalone)是部署 Flink 最基本也是最简单的方式,独立模式是独立运行的,不依赖任何外部的资源管理平台;但是如果资源不足,或者出现故障,没有自动扩展或重分配资源的保证,必须手动处理。所以独立模式一般只用在开发测试或作业非常少的场景下。
2.4 Yarn模式
2.4.1 相关配置
1.首先进入到/etc/profile.d
目录下,输入命令vi my_env.sh
在该文件中增加如下设置:
#HADOOP_CLASSPATH export HADOOP_CLASSPATH=`hadoop classpath`
2.设置好后记得source /etc/profile
使其生效。
注意:配置yarn时,只需要在hadoop102一台虚拟机上配置就好了,而且hadoop classpath的两个符号不是英文单引号而是飘号,就是tab键上面的那个,注意不要输错。
2.4.2 可用参数解读
- -d:分离模式,如果你不想让 Flink YARN 客户端一直前台运行,可以使用这个参数,即使关掉当前对话窗口,YARN session 也可以后台运行。
- -jm(--jobManagerMemory):配置 JobManager 所需内存,默认单位 MB。
- -nm(--name):配置在 YARN UI 界面上显示的任务名。
- -qu(--queue):指定 YARN 队列名。
- -tm(--taskManager):配置每个 TaskManager 所使用内存。
2.4.3 会话模式部署
提交任务命令:
# 先开启一个yarn对话 bin/yarn-session.sh -nm test # 提交任务 bin/flink run -c com.atguigu.wc.StreamWordCount FlinkTutorial-1.0-SNAPSHOT.jar
2.4.4 单作业模式部署
提交任务命令:bin/flink run -d -t yarn-per-job -c com.atguigu.wc.StreamWordCount FlinkTutorial-1.0-SNAPSHOT.jar
2.4.5 应用模式部署
提交任务命令: bin/flink run-application -t yarn-application -c com.atguigu.wc.StreamWordCount FlinkTutorial-1.0-SNAPSHOT.jar
三、Flink运行时架构
3.1 整体构成
Flink 的运行时架构中,最重要的就是两大组件:作业管理器(JobManger)和任务管理器(TaskManager)。对于一个提交执行的作业,JobManager 负责管理调度,在不考虑高可用的情况下只能有一个;而 TaskManager(Worker、Slave)负责执行任务处理数据,可以有一个或多个。注意客户端并不是处理系统的一部分,它只负责作业的提交。
3.1.1 作业管理器(JobManager)
JobManager 是一个 Flink 集群中任务管理和调度的核心,是控制应用执行的主进程。也就是说,每个应用都应该被唯一的 JobManager 所控制执行。当然,在高可用(HA)的场景下,可能会出现多个 JobManager;这时只有一个是正在运行的领导节点(leader),其他都是备用节点(standby)。JobManger 又包含 3 个不同的组件,分别是:
1. JobMaster
JobMaster 是 JobManager 中最核心的组件,负责处理单独的作业(Job)。JobMaster和具体的 Job 是一 一对应的,多个 Job 可以同时运行在一个 Flink 集群中, 每个 Job 都有一个自己的 JobMaster。在运行过程中,JobMaster 会负责所有需要中央协调的操作。
2. 资源管理器(ResourceManager)
ResourceManager 主要负责资源的分配和管理,在 Flink 集群中只有一个。所谓“资源”,主要是指 TaskManager 的任务槽(task slots)。任务槽就是 Flink 集群中的资源调配单元,包含了机器用来执行计算的一组 CPU 和内存资源。每一个任务(Task)都需要分配到一个 slot 上执行。 注意把 Flink 内置的 ResourceManager 和其他资源管理平台(比如 YARN)的 ResourceManager 区分开。Flink 的 ResourceManager,针对不同的环境和资源管理平台(比如 Standalone 部署,或者YARN),有不同的具体实现。另外,ResourceManager 还负责停掉空闲的 TaskManager,释放计算资源。
3. 分发器(Dispatcher)
Dispatcher 主要负责提供一个 REST 接口,用来提交应用,并且负责为每一个新提交的作业启动一个新的 JobMaster 组件。Dispatcher 也会启动一个 Web UI,用来方便地展示和监控作业执行的信息。Dispatcher 在架构中并不是必需的,在不同的部署模式下可能会被忽略掉。
3.1.2 任务管理器(TaskManager)
TaskManager 是 Flink 中的工作进程,数据流的具体计算就是它来做的,所以也被称为“Worker”。Flink 集群中必须至少有一个 TaskManager。每一个 TaskManager 都包含了一定数量的任务槽(task slots)。Slot是资源调度的最小单位,slot 的数量限制了 TaskManager 能够并行处理的任务数量。 启动之后,TaskManager 会向资源管理器注册它的 slots;收到资源管理器的指令后TaskManager 就会将一个或者多个槽位提供给 JobMaster 调用,JobMaster 就可以分配任务来执行了。 在执行过程中,TaskManager 可以缓冲数据,还可以跟其他运行同一应用的 TaskManager交换数据。
3.2 作业提交流程
Flink 的提交流程,随着部署模式、资源管理平台的不同,会有不同的变化。抽象的提交流程大致如下:
3.2.1 独立模式
在独立模式(Standalone)下,只有会话模式和应用模式两种部署方式。会话模式是预先启动,应用模式则是在作业提交时启动。提交流程如图所示:
3.2.2 Yarn模式
1.会话模式
2.单作业模式
3.应用模式
应用模式与单作业模式的提交流程非常相似,只是初始提交给 YARN 资源管理器的不再是具体的作业,而是整个应用。
3.3 一些重要概念
3.3.1 数据流图(Dataflow Graph)
在 Flink 代码中,我们定义的每一个处理转换操作都叫作“算子”(Operator),所以我们的程序可以看作是一串算子构成的管道,数据则像水流一样有序地流过。所有的 Flink 程序都可以归纳为由三部分构成:Source、Transformation 和 Sink。
- Source 表示“源算子”,负责读取数据源。
- Transformation 表示“转换算子”,利用各种算子进行处理加工。
- Sink 表示“下沉算子”,负责数据的输出。
在运行时,Flink 程序会被映射成所有算子按照逻辑顺序连接在一起的一张图,这被称为“逻辑数据流”(logical dataflow),或者叫“数据流图”(dataflow graph)。除了 Source 读取数据和 Sink 输出数据,一个中间的转换算子(Transformation Operator)必须是一个转换处理的操作;
3.3.2 并行度(Parallelism)
一个特定算子的子任务(subtask)的个数被称之为其并行度(parallelism)。包含并行子任务的数据流,就是并行数据流,它需要多个分区(stream partition)来分配并行任务。一般情况下,一个流程序的并行度,可以认为就是其所有算子中最大的并行度。一个程序中,不同的算子可能具有不同的并行度。
3.3.3 算子链
1.算子间的数据传输
一个数据流在算子之间传输数据的形式可以是一对一(one-to-one)的直通 (forwarding)模式,也可以是打乱的重分区(redistributing)模式,具体是哪一种形式,取决于算子的种类。
(1)一对一(One-to-one,forwarding)
这种模式下,数据流维护着分区以及元素的顺序。即算子之间是一对一的关系,这种关系类似于 Spark 中的窄依赖。
(2)重分区(Redistributing)
在这种模式下,数据流的分区会发生改变。这一过程类似于 Spark 中的 shuffle。总体来说,这种算子间的关系类似于 Spark 中的宽依赖。
2. 合并算子链
在 Flink 中,并行度相同的一对一(one to one)算子操作,可以直接链接在一起形成一个“大”的任务(task),这样原来的算子就成为了真正任务里的一部分,每个 task 会被一个线程执行。这样的技术被称为“算子链”(Operator Chain)。将算子链接成 task 是非常有效的优化:可以减少线程之间的切换和基于缓存区的数据交换,在减少时延的同时提升吞吐量。 Flink 默认会按照算子链的原则进行链接合并,如果我们想要禁止合并或者自行定义,也可以在代码中对算子做一些特定的设置。
3.3.4 作业图(JobGraph)与执行图(ExecutionGraph)
由 Flink 程序直接映射成的数据流图(dataflow graph),也被称为逻辑流图(logical StreamGraph),因为它们表示的是计算逻辑的高级视图。到具体执行环节时,我们还要考虑并行子任务的分配、数据在任务间的传输,以及合并算子链的优化。为了说明最终应该怎样执行一个流处理程序,Flink 需要将逻辑流图进行解析,转换为物理数据流图。 在这个转换过程中,有几个不同的阶段,会生成不同层级的图,其中最重要的就是作业图(JobGraph)和执行图(ExecutionGraph)。Flink 中任务调度执行的图,按照生成顺序可以分成四层:
逻辑流图(StreamGraph)→ 作业图(JobGraph)→ 执行图(ExecutionGraph)→ 物理图(Physical Graph)。
1.逻辑流图(StreamGraph)
这是根据用户通过 DataStream API 编写的代码生成的最初的 DAG 图,用来表示程序的拓扑结构。这一步一般在客户端完成。
2.作业图(JobGraph)
StreamGraph 经过优化后生成的就是作业图(JobGraph),这是提交给 JobManager 的数据结构,确定了当前作业中所有任务的划分。主要的优化为: 将多个符合条件的节点链接在一起合并成一个任务节点,形成算子链,这样可以减少数据交换的消耗。JobGraph 一般也是在客户端生成的,在作业提交时传递给 JobMaster。
3.执行图(ExecutionGraph)
JobMaster 收到 JobGraph 后,会根据它来生成执行图(ExecutionGraph)。ExecutionGraph是 JobGraph 的并行化版本,是调度层最核心的数据结构。
4.物理图(Physical Graph)
JobMaster 生成执行图后, 会将它分发给 TaskManager;各个 TaskManager 会根据执行图部署任务,最终的物理执行过程也会形成一张“图”,一般就叫作物理图(Physical Graph)。这只是具体执行层面的图,并不是一个具体的数据结构。物理图主要就是在执行图的基础上,进一步确定数据存放的位置和收发的具体方式。有了物理图,TaskManager 就可以对传递来的数据进行处理计算了。
3.3.5 任务(Tasks)和任务槽(Task Slots)
1.任务槽
为了控制并发量,我们需要在 TaskManager 上对每个任务运行所占用的资源做出明确的划分,这就是所谓的任务槽(task slots)每个任务槽(task slot)其实表示了 TaskManager 拥有计算资源的一个固定大小的子集。这些资源就是用来独立执行一个子任务的。
2.任务槽的设置
我们可以通过集群的配置文件来设定TaskManager的slot数量:taskmanager.numberOfTaskSlots: 8
,slot 目前仅仅用来隔离内存,不会涉及 CPU 的隔离。在具体应用时,可以将 slot 数量配置为机器的 CPU 核心数,尽量避免不同任务之间对 CPU 的竞争。这也是开发环境默认并行度设为机器 CPU 数量的原因。
3.任务对任务槽的共享
默认情况下,Flink 是允许子任务共享 slot 的。只要属于同一个作业,那么对于不同任务节点的并行子任务,就可以放到同一个 slot 上执行。一个 slot 中,可以将程序处理的所有任务都放在这里执行,我们把它叫作保存了整个作业的运行管道(pipeline)。slot 共享另一个好处就是允许我们保存完整的作业管道。这样一来,即使某个 TaskManager出现故障宕机,其他节点也可以完全不受影响,作业的任务可以继续执行。
4.任务槽和并行度的关系
task slot是静态的概念,是指 TaskManager 具有的并发执行能力,可以通过参数taskmanager.numberOfTaskSlots 进行配置;而并行度(parallelism)是动态概念,也就是TaskManager 运行程序时实际使用的并发能力,可以通过参数 parallelism.default 进行配置。
四、DataStream API基础篇
一个 Flink 程序,其实就是对 DataStream 的各种转换。具体来说,代码基本上都由以下几部分构成:
- 获取执行环境(execution environment)
- 读取数据源(source)
- 定义基于数据的转换操作(transformations)
- 定义计算结果的输出位置(sink)
- 触发程序执行(execute)
4.1 执行环境(Execution Environment)
4.1.1 创建执行环境
在代码中创建执行环境的方式,就是调用 StreamExecutionEnvironment 类的静态方法,具体有以下三种:
1. getExecutionEnvironment
这个方法会根据当前运行的方式,自行决定该返回什么样的运行环境,用起来简单高效,是最常用的一种创建执行环境的方式。
示例:StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
2. createLocalEnvironment
这个方法返回一个本地执行环境。可以在调用时传入一个参数,指定默认的并行度;如果不传入,则默认并行度就是本地的 CPU 核心数。
示例:StreamExecutionEnvironment localEnv = StreamExecutionEnvironment.createLocalEnvironment();
3. createRemoteEnvironment
这个方法返回集群执行环境。需要在调用时指定 JobManager 的主机名和端口号,并指定要在集群中运行的 Jar 包。
示例:
StreamExecutionEnvironment remoteEnv = StreamExecutionEnvironment .createRemoteEnvironment( "host", // JobManager 主机名 1234, // JobManager 进程端口号 "path/to/jarFile.jar" // 提交给 JobManager 的 JAR 包 );
4.1.2 执行模式(Execution Mode)
可以通过设置“执行模式”(execution mode)使Flink 程序在流处理和批处理之间切换。
- 流执行模式(STREAMING)
这是 DataStream API 最经典的模式,一般用于需要持续实时处理的无界数据流。默认情况下,程序使用的就是 STREAMING 执行模式。 - 批执行模式(BATCH)
专门用于批处理的执行模式, 这种模式下,Flink 处理作业的方式类似于 MapReduce 框架。对于不会持续计算的有界数据,我们用这种模式处理会更方便。(batch模式可以通过命令行和代码配置,但是在代码中设置可拓展性较差,一般不推荐代码设置)虽然流执行模式对所有的数据都使用,但是对于批数据来说有时候直接输出结果更加高效。 - 自动模式(AUTOMATIC)
在这种模式下,将由程序根据输入数据源是否有界,来自动选择执行模式。
4.1.3 触发程序执行
显示调用执行环境的execution方法,env.execute();
4.2 源算子(Source)
源算子(source operator)就是读取数据的算子。Flink 代码中通用的添加 source 的方式,是调用执行环境的 addSource()方法:DataStream<String> stream = env.addSource(...);
4.2.1 从集合中读取数据
构建集合然后然后调用执行环境的 fromCollection 方法进行读取。
DataStream<Event> stream = env.fromCollection(ArrayList);
我们也可以不构建集合,直接将元素列举出来,调用 fromElements 方法进行读取数据:
DataStreamSource<Event> stream2 = env.fromElements( new Event("Mary", "./home", 1000L), new Event("Bob", "./cart", 2000L) );
4.2.2 从文件中读取数据
实际应用中读取文件数据是最多的。一个比较常见的方式就是读取日志文件。这也是批处理中最常见的读取方式:
DataStream<String> stream = env.readTextFile("clicks.csv");
4.2.3 从 Socket 读取数据
读取socket文本流:DataStream<String> stream = env.socketTextStream("localhost", 7777);
4.2.4 从 Kafka 读取数据
emmm,kafka还没学,也看不懂,等学了再回来补哈
4.2.5 自定义 Source
遇到特殊情况可能需要自定义实现 SourceFunction 。首先要创建一个自定义的数据源,实现 SourceFunction 接口。主要重写两个关键方法:run()和 cancel()。
- run()方法:使用运行时上下文对象(SourceContext)向下游发送数据;
- cancel()方法:通过标识位控制退出循环,来达到中断数据源的效果。
4.2.6 Flink支持的数据类型
由于数据类型繁多杂乱,所以为了方便地处理数据Flink 使用“类型信息”(TypeInformation)来统一表示数据类型。TypeInformation 类是 Flink 中所有类型描述符的基类。它涵盖了类型的一些基本属性,并为每个数据类型生成特定的序列化器、反序列化器和比较器。
1. Flink支持的数据类型
(1)基本类型
所有 Java 基本类型及其包装类,再加上 Void、String、Date、BigDecimal 和 BigInteger。
(2)数组类型
包括基本类型数组(PRIMITIVE_ARRAY)和对象数组(OBJECT_ARRAY) 。
(3)复合数据类型
Java 元组类型(TUPLE),Scala 样例类及 Scala 元组,行类型(ROW),POJO等。
(4)辅助类型
Option、Either、List、Map 等。
(5)泛型类型(GENERIC)
Flink 会把泛型类型当作黑盒,无法获取它们内部的属性;它们也不是由 Flink 本身序列化的,而是由 Kryo 序列化的。
2. 类型提示(Type Hints)
Flink 专门提供了 TypeHint 类,它可以捕获泛型的类型信息,并且一直记录下来,为运行时提供足够的信息。我们同样可以通过.returns()方法,明确地指定转换之后的 DataStream 里元素的类型。
4.3 转换算子(Transformation)
一个 Flink 程序的核心,其实就是所有的转换操作,它们决定了处理的业务逻辑。
4.3.1 基本转换算子
1. 映射(map)
基于 DataStrema 调用 map()方法进行转换处理。方法需要传入的参数是接口 MapFunction 的实现,返回值类型是 DataStream。
2. 过滤(filter)
filter 转换操作,顾名思义是对数据流执行一个过滤,通过一个布尔条件表达式设置过滤条件,对于每一个流内元素进行判断,若为 true 则元素正常输出,若为 false 则元素被过滤掉。
3. 扁平映射(flatMap)
flatMap 操作又称为扁平映射,主要是将数据流中的整体(一般是集合类型)拆分成一个一个的个体使用。也就是先按照某种规则对数据进行打散拆分,再对拆分后的元素做转换处理。
4.3.2 聚合算子(Aggregation)
1. 按键分区(keyBy)
在Flink中 DataStream 没有直接进行聚合的 API,所以做聚合之前要先用keyBy分区,keyBy 是聚合前必须要用到的一个算子。keyBy 通过指定键(key),可以将一条流从逻辑上划分成不同的分区(partitions)。这里所说的分区,其实就是并行处理的子任务,也对应着任务槽(task slot)。
keyBy得到的是KeyedStream,KeyedStream可以认为是“分区流”或者“键控流”,也继承自 DataStream,KeyedStream 是一个非常重要的数据结构,只有基于它才可以做后续的聚合操作(比如 sum,reduce)。
2.简单聚合
Flink 内置实现了一些最基本、最简单的聚合 API,主要有以下几种:
- sum():在输入流上,对指定的字段做叠加求和的操作。
- min():在输入流上,对指定的字段求最小值。
- max():在输入流上,对指定的字段求最大值。
- minBy():与 min()类似,在输入流上针对指定字段求最小值。不同的是,min()只计算指定字段的最小值,其他字段会保留最初第一个数据的值;而 minBy()则会返回包含字段最小值的整条数据。
- maxBy():与 max()类似,在输入流上针对指定字段求最大值。两者区别与min()/minBy()完全一致。
注:keyBy 和聚合是成对出现的,先分区、后聚合,得到的依然是一个 DataStream。
3. 归约聚合(reduce)
对已有的数据进行归约处理,把每一个新输入的数据和当前已经归约出来的值,再做一个聚合计算。
4.3.3 用户自定义函数(UDF)
可以通过自定义函数类或者匿名类实现接口,也可以直接传入 Lambda 表达式。这就是用户自定义函数(user-defined function,UDF)。
1.函数类(Function Classes)
2. 匿名函数(Lambda)
匿名函数(Lambda 表达式)是 Java 8 引入的新特性,方便我们更加快速清晰地写代码。 Lambda 表达式允许以简洁的方式实现函数,以及将函数作为参数来进行传递,而不必声明额外的(匿名)类。
3. 富函数类(Rich Function Classes)
“富函数类”也是 DataStream API 提供的一个函数类的接口,所有的 Flink 函数类都有其Rich 版本。富函数类一般是以抽象类的形式出现的。与常规函数类的不同主要在于,富函数类可以获取运行环境的上下文,并拥有一些生命周期方法,所以可以实现更复杂的功能。
4.3.4 物理分区(Physical Partitioning)
“分区”(partitioning)操作就是要将数据进行重新分布,传递到不同的流分区去进行下一步处理。如果说 keyBy 这种逻辑分区是一种“软分区”,那真正硬核的分区就应该是所谓的“物理分区”(physical partitioning)。分区算子并不对数据进行转换处理,只是定义了数据的传输方式。 常见的物理分区策略有随机分配(Random)、轮询分配(Round-Robin)、重缩放(Rescale)和广播(Broadcast)。
1.随机分区(shuffle)
最简单的重分区方式就是直接“洗牌”。通过调用 DataStream 的.shuffle()方法,随机分区服从均匀分布(uniform distribution),把流中的数据随机打乱,均匀地传递到下游任务分区,对于同样的输入数据, 每次执行得到的结果也不会相同。
2. 轮询分区(Round-Robin)
轮询也是一种常见的重分区方式。简单来说就是“发牌”,按照先后顺序将数据做依次分发,通过调用 DataStream 的.rebalance()方法,就可以实现轮询重分区。rebalance使用的是 Round-Robin 负载均衡算法,可以将输入流数据平均分配到下游的并行任务中去。
3. 重缩放分区(rescale)
重缩放分区和轮询分区非常相似。当调用 rescale()方法时,其实底层也是使用 Round-Robin算法进行轮询,但是只会将数据轮询发送到下游并行任务的一部分中,由于 rebalance 是所有分区数据的“重新平衡”,当 TaskManager 数据量较多时,这种跨节点的网络传输必然影响效率;用 rescale 的方式进行“局部重缩放”,可以让数据只在当前 TaskManager 的多个 slot 之间重新分配,从而避免了网络传输带来的损耗。
4. 广播(broadcast)
经过广播之后,数据会在不同的分区都保留一份,可能进行重复处理。可以通过调用 DataStream 的 broadcast()方法,将输入数据复制并发送到下游算子的所有并行任务中去。
5. 全局分区(global)
全局分区也是一种特殊的分区方式,通过调用.global()方法,将所有的输入流数据都发送到下游算子的第一个并行子任务中去。相当于强行让下游任务并行度变成了 1,所以使用这个操作需要非常谨慎,可能对程序造成很大的压力。
6. 自定义分区(Custom)
当 Flink 提供的所有分区策略都不能满足用户的需求时,我们可以通过使用partitionCustom()方法来自定义分区策略。 在调用时,方法需要传入两个参数,第一个是自定义分区器(Partitioner)对象,第二个是应用分区器的字段。
4.4 输出算子(Sink)
4.4.1 连接到外部系统
Flink 的 DataStream API 专门提供了向外部写入数据的方法:addSink,主要就是用来实现与外部系统连接、并将数据提交写入的;Flink 程序中所有对外的输出操作,一般都是利用 Sink 算子完成的。一般情况下 Sink 算子的创建是通过调用 DataStream 的.addSink()方法实现的:stream.addSink(new SinkFunction(…));
。
4.4.2 输出到文件
Flink 为此专门提供了一个流式文件系统的连接器:StreamingFileSink,StreamingFileSink 支持行编码(Row-encoded)和批量编码(Bulk-encoded,比如 Parquet)格式。这两种不同的方式都有各自的构建器(builder),调用方法也非常简单,可以直接调用StreamingFileSink 的静态方法:
- 行编码:
StreamingFileSink.forRowFormat(basePath,rowEncoder)
。 - 批量编码:
StreamingFileSink.forBulkFormat(basePath,bulkWriterFactory)
。
在创建行或批量编码 Sink 时,我们需要传入两个参数,用来指定存储桶的基本路径(basePath)和数据的编码逻辑(rowEncoder 或 bulkWriterFactory)。
4.4.3 第三方系统连接器
可以将数据输出到Kafka,Redis,Elasticsearch,MySQL(JDBC),详情请自己查询。
4.4.4 自定义 Sink 输出
Flink 为我们提供了通用的 SinkFunction 接口和对应的 RichSinkDunction抽象类,只要实现它,通过简单地调用 DataStream 的.addSink()方法就可以自定义写入任何外部存储。在实现 SinkFunction 的时候,需要重写的一个关键方法 invoke(),在这个方法中我们就可以实现将流里的数据发送出去的逻辑。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· NetPad:一个.NET开源、跨平台的C#编辑器