vlambda博客
学习文章列表

一文搞懂Flink的基础知识点

点击蓝字

2022

今天分享的这篇文章是关于Flink的基础知识,包括基本概念、设计理念、架构模型、编程模型、常用算子。


1、什么是Flink?简单描述下


Flink是一个以 流 为核心的高可用、高性能的分布式计算引擎。具备 流批一体,高吞吐、低延迟,容错能力,大规模复杂计算等特点,在数据流上提供 数据分发、通信等功能。


2、解释下其中的 数据流、流批一体、容错能力等概念?


数据流:所有产生的 数据 都天然带有 时间概念,把 事件 按照时间顺序排列起来,就形成了一个事件流,也被称作数据流。


流批一体:


首先必须先明白什么是 有界数据 和 无界数据


一文搞懂Flink的基础知识点


有界数据,就是在一个确定的时间范围内的数据流,有开始,有结束,一旦确定就不会再改变,一般 批处理 用来处理有界数据,如上图的 bounded stream。


无界数据,就是持续产生的数据流,数据是无限的,有开始,无结束,一般 流处理 用来处理无界数据。如图 unbounded stream。


Flink的设计思想是以 流 为核心,批是流的特例,擅长处理 无界 和 有界 数据, Flink 提供 精确的时间控制能力 和 有状态 计算机制,可以轻松应对无界数据流,同时 提供 窗口 处理有界数据流。所以被成为流批一体。


容错能力:


在分布式系统中,硬件故障、进程异常、应用异常、网络故障等异常无处不在,Flink引擎必须保证故障发生后 不仅可以 重启 应用程序,还要 确保 其内部状态保持一致,从最后一次正确的时间点重新出发


Flink提供 集群级容错 和 应用级容错 能力


集群级容错:Flink 与 集群管理器紧密连接,如YARN、Kubernetes,当进程挂掉后,自动重启新进程接管之前的工作。同时具备 高可用性 ,可消除所有单点故障,


应用级容错:Flink 使用 轻量级分布式快照,设计检查点(checkpoint)实现可靠容错。


Flink 利用检查点特性,在框架层面 提供 Exactly-once 语义,即端到端的一致性,确保数据仅处理一次,不会重复也不会丢失,即使出现故障,也能保证数据只写一次。


3、Flink 和 Spark Streaming的区别?


Flink 和 Spark Sreaming 最大的区别在于:


Flink 是标准的实时处理引擎,基于事件驱动,以流为核心,


Spark Streaming 的RDD 实际是一组小批次的RDD集合,是微批(Micro-Batch)的模型,以批为核心。


下面我们介绍两个框架的主要区别:


1、架构模型


Spark Streaming 在运行时的主要角色包括:


服务架构集群和资源管理 Master / Yarn Application Master;


工作节点 Work / Node Manager;


任务调度器 Driver;任务执行器 Executor


一文搞懂Flink的基础知识点


Flink 在运行时主要包含:客户端 Client、作业管理 Jobmanager、任务管理Taskmanager。



2、任务调度

Spark Streaming 连续不断的生成微小的数据批次,构建有向无环图DAG,Spark Streaming 会依次创建 DStreamGraph、JobScheduler。


Flink 根据用户提交的代码生成 StreamGraph,经过优化生成 JobGraph,然后提交给 JobManager进行处理,JobManager 会根据 JobGraph 生成 ExecutionGraph,ExecutionGraph 是 Flink 调度最核心的数据结构,JobManager 根据 ExecutionGraph 对 Job 进行调度,根据物理执行图部署到Taskmanager上形成具体的Task执行。



3、时间机制


Spark Streaming 支持的时间机制有限,只支持处理时间。


Flink 支持了流处理程序在时间上的三个定义:事件时间 EventTime、摄入时间 IngestionTime 、处理时间 ProcessingTime。同时也支持 watermark 机制来处理滞后数据。

一文搞懂Flink的基础知识点


4、容错机制


对于 Spark Streaming 任务,我们可以设置 checkpoint,然后假如发生故障并重启,我们可以从上次 checkpoint 之处恢复,但是这个行为只能使得数据不丢失,可能会重复处理,不能做到恰好一次处理语义。


Flink 则使用两阶段提交协议来解决这个问题。


4、Flink 的架构包含哪些?


Flink 架构分为 技术架构 和 运行架构 两部分。


1、技术架构


如下图为Flink技术架构:



Flink 作为流批一体的分布式计算引擎,必须提供面向开发人员的API层,同时还需要跟外部数据存储进行交互,需要连接器,作业开发、测试完毕后,需要提交集群执行,需要部署层,同时还需要运维人员能够管理和监控,还提供图计算、机器学习、SQL等,需要应用框架层。


2、运行架构


如下图为Flink运行架构:


一文搞懂Flink的基础知识点


Flink 集群采取 Master-Slave 架构,Master的角色为 JobManager,负责集群和作业管理,Slave的角色是 TaskManager,负责执行计算任务,同时,Flink 提供客户端 Client 来管理集群和提交任务,JobManager 和 TaskManager 是集群的进程。


(1)Client


Flink 客户端是Flink 提供的 CLI 命令行工具,用来提交 Flink 作业到 Flink 集群,在客户端中负责 StreamGraph (流图)和 JobGraph (作业图)的构建。


(2)JobManager


JobManager 根据并行度将 Flink 客户端提交的Flink 应用分解为子任务,从资源管理器 ResourceManager 申请所需的计算资源,资源具备之后,开始分发任务到 TaskManager 执行 Task,并负责应用容错,跟踪作业的执行状态,发现异常则恢复作业等。


(3)TaskManager


TaskManager 接收 JobManage 分发的子任务,根据自身的资源情况 管理子任务的启动、 停止、销毁、异常恢复等生命周期阶段。Flink程序中必须有一个TaskManager。


5、Flink 的并行度是什么,介绍一下?


Flink程序在执行的时候,会被映射成一个Streaming Dataflow。一个Streaming Dataflow是由一组Stream和Transformation Operator组成的。在启动时从一个或多个Source Operator开始,结束于一个或多个Sink Operator。


Flink程序本质上是并行的和分布式的,在执行过程中,一个流(stream)包含一个或多个流分区,而每一个operator包含一个或多个operator子任务。操作子任务间彼此独立,在不同的线程中执行,甚至是在不同的机器或不同的容器上。


operator子任务的数量是这一特定operator的并行度。相同程序中的不同operator有不同级别的并行度。


一文搞懂Flink的基础知识点

一个Stream可以被分成多个Stream的分区,也就是Stream Partition。一个Operator也可以被分为多个Operator Subtask。


如上图中,Source被分成Source1和Source2,它们分别为Source的Operator Subtask。每一个Operator Subtask都是在不同的线程当中独立执行的。一个Operator的并行度,就等于Operator Subtask的个数。


上图Source的并行度为2。而一个Stream的并行度就等于它生成的Operator的并行度。数据在两个operator之间传递的时候有两种模式:


(1)One to One模式:两个operator用此模式传递的时候,会保持数据的分区数和数据的排序;如上图中的Source1到Map1,它就保留的Source的分区特性,以及分区元素处理的有序性。


(2)Redistributing (重新分配)模式:这种模式会改变数据的分区数;每个operator subtask会根据选择 transformation 把数据发送到不同的目标subtasks,比如keyBy()会通过hashcode重新分区,broadcast()和rebalance()方法会随机重新分区。


6、Flink 的并行度的怎么设置的?


在实际生产环境中可以从四个不同层面设置并行度:


操作算子层面(Operator Level)


执行环境层面(Execution Environment Level)


客户端层面(Client Level)


系统层面(System Level)


需要注意的优先级:算子层面 > 环境层面 > 客户端层面 > 系统层面。


7、Flink 编程模型了解吗?


Flink 应用程序主要由三部分组成,源 Source、转换 transformation、目的地 sink。这些流式 dataflows 形成了有向图,以一个或多个源(source)开始,并以一个或多个目的地(sink)结束。


一文搞懂Flink的基础知识点


8、Flink 作业中的DataStream,Transformation介绍一下?


Flink作业中,包含两个基本的块:数据流(DataStream)和 转换(Transformation)。


DataStream是逻辑概念,为开发者提供API接口,Transformation是处理行为的抽象,包含了数据的读取、计算、写出。所以Flink 作业中的DataStream API 调用,实际上构建了多个由 Transformation组成的数据处理流水线(Pipeline)


DataStream API 和 Transformation 的转换如下图:


一文搞懂Flink的基础知识点


9、Flink 的分区策略了解吗?

目前 Flink 支持8种分区策略的实现,数据分区体系如下图:

一文搞懂Flink的基础知识点


(1)GlobalPartitioner


数据会被分发到下游算子的第一个实例中进行处理。


(2)ForwardPartitioner


在API层面上ForwardPartitioner应用在DataStream上,生成一个新的 DataStream。


该Partitioner 比较特殊,用于在同一个 OperatorChain 中上下游算子之间的数据转发,实际上数据是直接传递给下游的,要求上下游并行度一样。


(3)ShufflePartitioner


随机的将元素进行分区,可以确保下游的Task能够均匀地获得数据,使用代码如下:


dataStream.shuffle();

1

(4)RebalancePartitioner


以Round-robin 的方式为每个元素分配分区,确保下游的 Task 可以均匀地获得数据,避免数据倾斜。使用代码如下:


dataStream.rebalance();

1

(5)RescalePartitioner


根据上下游 Task 的数量进行分区, 使用 Round-robin 选择下游的一个Task 进行数据分区,如上游有2个 Source.,下游有6个 Map,那么每个 Source 会分配3个固定的下游 Map,不会向未分配给自己的分区写人数据。这一点与 ShufflePartitioner 和 RebalancePartitioner 不同, 后两者会写入下游所有的分区。


一文搞懂Flink的基础知识点


运行代码如下:


dataStream.rescale();

1

(6)BroadcastPartitioner


将该记录广播给所有分区,即有N个分区,就把数据复制N份,每个分区1份,其使用代码如下:


dataStream.broadcast();

1

(7)KeyGroupStreamPartitioner


在API层面上,KeyGroupStreamPartitioner应用在 KeyedStream上,生成一个新的 KeyedStream。


KeyedStream根据keyGroup索引编号进行分区,会将数据按 Key 的 Hash 值输出到下游算子实例中。该分区器不是提供给用户来用的。


KeyedStream在构造Transformation的时候默认使用KeyedGroup分区形式,从而在底层上支持作业Rescale功能。


(8)CustomPartitionerWrapper


用户自定义分区器。需要用户自己实现Partitioner接口,来定义自己的分区逻辑。


10、描述一下Flink wordcount执行包含的步骤有哪些?


主要包含以下几步:


(1)获取运行环境 StreamExecutionEnvironment


(2)接入source源


(3)执行转换操作,如map()、flatmap()、keyby()、sum()


(4)输出sink源,如print()


(5)执行 execute


提供一个示例:


一文搞懂Flink的基础知识点


11、Flink 常用的算子有哪些?

分两部分:


(1)数据读取,这是Flink流计算应用的起点,常用算子有:


从内存读:fromElements,从文件读:readTextFile,Socket 接入 :socketTextStream,也可以自定义读取:addSource,主要是从kafka获取数据


(2)处理数据的算子,主要用于 转换 过程


常用的算子包括:Map(单输入单输出)、FlatMap(单输入、多输出)、Filter(过滤)、KeyBy(分组)、Reduce(聚合)、Window(窗口)、Connect(连接)、Split(分割)等。


12、Flink 如何计算实时的 topN?


Flink 要实现 TopN 功能,主要做如下操作:


Flink 接收 kafka 数据源;


基于 EventTime 处理,指定 Watermark,这里调用 DataStream 的 assignTimestampsAndWatermarks 方法,抽取时间和设置 watermark。


将 kafka 的 json 格式数据转为实体类对象。


根据用户 Username 进行分组,对于实时统计 TopN 可以使用滑动窗口。设置窗口长度取 10s,每次滑动(slide)5s,即 5 秒钟更新一次过去 10s 的排名数据。


.keyBy("username")

.timeWindow(Time.seconds(10), Time.seconds(5))

.aggregate(new CountAgg(), new WindowResultFunction())



使用 .aggregate(AggregateFunction af, WindowFunction wf) 做增量的聚合操作,它能用 AggregateFunction 提前聚合掉数据,减少 state 的存储压力。


CountAgg 实现了 AggregateFunction 接口,功能是统计窗口中的条数,即遇到一条数据就加一。


WindowFunction 将每个 key 每个窗口聚合后的结果带上其他信息进行输出。这里实现的WindowResultFunction 将用户名,窗口,访问量封装成了 UserViewCount 进行输出。


为了统计每个窗口下活跃的用户,我们需要再次按窗口进行分组,根据 UserViewCount 中的 windowEnd 进行 keyBy() 操作。然后使用 ProcessFunction 实现一个自定义的 TopN 函数 TopNHotItems 来计算点击量排名前3名的用户,并将排名结果格式化成字符串,便于后续输出。


.keyBy("windowEnd")

.process(new TopNHotUsers(3))

.print();



ProcessFunction 是 Flink 提供的一个 low-level API,它主要提供定时器 timer 的功能。通过 timer 来判断何时收齐了某个 window 下所有用户的访问数据。由于 Watermark 的进度是全局的,在 processElement 方法中,每当收到一条数据ItemViewCount,就注册一个 windowEnd+1 的定时器 windowEnd+1 的定时器被触发时,意味着收到了 windowEnd+1 的 Watermark,即收齐了该 windowEnd 下的所有用户窗口统计值。然后使用 onTimer() 将收集的所有商品及点击量进行排序,选出 TopN,并将排名信息格式化成字符串后进行输出。


使用 ListState来存储收到的每条 UserViewCount 消息,保证在发生故障时,状态数据的不丢失和一致性。ListState 是 Flink 提供的类似 Java List 接口的 State API,它集成了框架 checkpoint 机制,可以保证 exactly-once 的语义。



一文搞懂Flink的基础知识点

往期精彩回顾

一文搞懂Flink的基础知识点






2022

图灵数科学园