学习笔记:Flink 理论知识

Flink 简介


Flink 是什么

Apache Flink 是一个框架和分布式处理引擎,用于在无边界和有边界数据流上进行有状态的计算。Flink 能在所有常见集群环境中运行,并能以内存速度和任意规模进行计算。
在这里插入图片描述


Flink 特点

事件驱动(Event-driven)

事件驱动型应用是一类具有状态的应用,它从一个或多个事件流提取数据,并根据到来的事件触发计算、状态更新或其他外部动作。
在这里插入图片描述

基于流的世界观

在 Flink 的世界观中,一切都是由流组成的,离线数据是有界限的流,实时数据是一个没有界限的流,这就是所谓的有界流和无界流。

  • 无界流:有定义流的开始,但没有定义流的结束。无界流的数据必须持续处理,即数据被摄取后需要立刻处理。处理无界数据通常要求以特定顺序摄取事件,例如事件发生的顺序,以便能够推断结果的完整性。
  • 有界流:有定义流的开始,也有定义流的结束。有界流可以在摄取所有数据后再进行计算。有界流所有数据可以被排序,所以并不需要有序摄取。有界流处理通常被称为批处理。

这种以流为世界观的架构,获得的最大好处就是具有极低的延迟。
在这里插入图片描述

分层 API

Flink 根据抽象程度分层,提供了三种不同的 API。
在这里插入图片描述

  • ProcessFunction 可以处理一或两条输入数据流中的单个事件或者归入一个特定窗口内的多个事件。它提供了对于时间和状态的细粒度控制。
  • DataStream API 为许多通用的流处理操作提供了处理原语。这些操作包括窗口、逐条记录的转换操作,在处理事件时进行外部数据库查询等。
  • Flink 支持两种关系型的 API,Table API 和 SQL。这两个 API 都是批处理和流处理统一的 API,这意味着在无边界的实时数据流和有边界的历史记录数据流上,关系型 API 会以相同的语义执行查询,并产生相同的结果。

其他特点

  • 支持事件时间(event-time)和处理时间(processing-time)语义
  • 精确一次(exactly-once)的状态一致性保证
  • 低延迟,每秒处理数百万个事件,毫秒级延迟
  • 与众多常用存储系统的连接
  • 高可用,动态扩展,实现7*24小时全天候运行

Flink vs Spark

流和微批

  • Spark:Micro Batching 计算模式认为流是批的特例, 流计算就是将连续不断的批进行持续计算,如果批足够小那么就有足够小的延时,在一定程度上满足了99%的实时计算场景。
  • Flink:Native Streaming 计算模式认为批是流的特例,每条数据的到来都进行计算,这种计算模式显得更自然,并且延时性能达到更低。

数据模型

  • Spark 采用 RDD 模型,spark streaming 的 DStream 实际上也就是一组组小批数据 RDD 的集合。
  • Flink 的基本数据模型是数据流及事件(Event)的序列。

运行架构

  • Spark 是批计算,将 DAG 划分为不同的 stage,一个完成后才可以计算下一个。
  • Flink 是标准的流执行模式,一个事件在一个节点处理完后可以直接发往下一个节点进行处理。

Flink 部署


Flink 安装和配置

下载地址:https://flink.apache.org/zh/downloads.html

下载压缩包后解压:
在这里插入图片描述
bin:binary,存放脚本、命令、二进制文件

  • start-cluster.sh:启动 Flink 集群
  • stop-cluster.sh:关闭 Flink 集群
  • jobmanager.sh:作业管理器,相当于 driver
  • taskmanager.sh:任务管理器,相当于 executor

conf:config,配置文件

  • flink-conf.yaml:配置的核心文件
# 通信地址和通信端口,一般不用改
jobmanager.rpc.address: localhost
jobmanager.rpc.port: 6123
# 每个 TaskManager 提供的任务插槽个数,相当于最大能够并行的数量
taskmanager.numberOfTaskSlots: 1
# 默认并行度,相当于真正执行时的并行的数量
parallelism.default: 1

lib:library,额外支持的库


Standalone 模式

启动/关闭 Flink 集群:

cd /mnt/d/Develop/Flink/flink-1.10.1
./bin/start-cluster.sh
./bin/stop-cluster.sh

Flink 架构也遵循 Master-Slave 架构设计原则,JobManager 为 Master 节点,TaskManager 为 Worker(Slave)节点。
在这里插入图片描述
访问:http://localhost:8081 可以对 Flink 集群和任务进行监控管理。
在这里插入图片描述
将程序生成 Jar 包:
在这里插入图片描述
Web UI 提交 Job

  • Entry Class:程序的入口类,如 wc.StreamWordCount
  • Program Arguments:程序启动参数,如 --host localhost --port 9999
  • Parallelism:默认并行度
  • Savepoint Path:手动检查点

在这里插入图片描述
提交失败,原因是 Slot 不足,在 flink-conf.yaml 文件尝试修改资源分配:

taskmanager.numberOfTaskSlots: 4

重新启动 Flink 集群,并提交 Job:在这里插入图片描述
输入数据,显示结果:
在这里插入图片描述
命令行提交 Job

# 提交 Job
# ./bin/flink run -c <Entry Class> -p <Parallelism> <Path> <Program Arguments>
./bin/flink run -c wc.StreamWordCount -p 3 /mnt/d/Users/Desktop/FlinkDemo-1.0-SNAPSHOT.jar --host localhost --port 9999

# 查看已提交的所有 Job
./bin/flink list

# 取消 Job
# ./bin/flink cancel <JobID>
./bin/flink cancel 09c731181e34852c2ba93633e19f7692

在这里插入图片描述
总共有 7 个 Task,为什么可以用 3 个 Slot 就能跑起来?
在这里插入图片描述


YARN 模式

Flink 提供了两种在 YARN 上运行的模式,分别为 Session-Cluster 模式和 Per-Job-Cluster 模式。

Session-Cluster 模式

Session-Cluster 模式需要先启动集群,然后再提交作业,接着会向 YARN 申请一 块空间后,资源永远保持不变。如果资源满了,下一个作业就无法提交,只能等到 YARN 中的其中一个作业执行完成后,释放了资源,下个作业才会正常提交。所有作业共享 Dispatcher 和 ResourceManager,共享资源;适合规模小执行时间短的作业。

在 YARN 中初始化一个 Flink 集群,开辟指定的资源,以后提交任务都向这里提交。这个 Flink 集群会常驻在 YARN 集群中,除非手工停止。

在这里插入图片描述

Per-Job-Cluster 模式

一个 Job 会对应一个集群,每提交一个作业会根据自身的情况,都会单独向 YARN 申请资源,直到作业执行完成,一个作业的失败与否并不会影响下一个作业的正常提交和运行。独享 Dispatcher 和 ResourceManager,按需接受资源申请;适合规模大长时间运行的作业。

每次提交都会创建一个新的 Flink 集群,任务之间互相独立,互不影响,方便管理。任务执行完成之后创建的集群也会消失。
在这里插入图片描述


Flink 运行架构


Flink 四大组件

Flink 运行时架构主要包括四个不同的组件,它们会在运行流处理应用程序时协同工作:作业管理器(JobManager)、资源管理器(ResourceManager)、任务管理器(TaskManager),以及分发器(Dispatcher)。因为 Flink 是用 Java 和 Scala 实现的,所以所有组件都会运行在Java 虚拟机上。

作业管理器(JobManager)

  • 控制一个应用程序执行的主进程,也就是说,每个应用程序都会被一个不同的 JobManager 所控制执行。
  • JobManager 会先接收到要执行的应用程序,这个应用程序会包括:作业图(JobGraph)、逻辑数据流图(Logical Dataflow Graph)和打包了所有的类、库和其它资源的 Jar 包。
  • JobManager 会把 JobGraph 转换成一个物理层面的数据流图,这个图被叫做执行图(ExecutionGraph),包含了所有可以并发执行的任务。
  • JobManager 会向 ResourceManager 请求执行任务必要的资源,也就是 TaskManager 上的插槽。一旦它获取到了足够的资源,就会将执行图分发到真正运行它们的 TaskManager 上。
  • 而在运行过程中,JobManager 会负责所有需要中央协调的操作,比如说检查点(checkpoints)的协调。

资源管理器(ResourceManager)

  • 主要负责管理 TaskManager 的插槽,TaskManger 插槽是 Flink 中定义的处理资源单元。
  • Flink 为不同的环境和资源管理工具提供了不同资源管理器,比如 YARN、Mesos、Kubernetes,以及 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 在架构中可能并不是必需的,这取决于应用提交运行的方式。

任务提交流程

在这里插入图片描述
如果部署的集群环境不同(例如 YARN,Mesos,Kubernetes,standalone 等),其中一些步骤可以被省略,或是有些组件会运行在同一个 JVM 进程中。

将 Flink 集群部署到 YARN 上:
在这里插入图片描述

  1. Flink 任务提交后,Client 向 HDFS 上传 Flink 的 Jar 包和配置。
  2. 之后,Client 向 YARN ResourceManager 提交 Job,ResourceManager 分配 Container 资源并通知对应的 NodeManager 启动 ApplicationMaster。
  3. ApplicationMaster 启动后,加载 Flink 的 Jar 包和配置构建环境,然后启动 JobManager。
  4. 之后,ApplicationMaster 向 ResourceManager 申请资源启动 TaskManager(JobManager 向 Flink RM 申请资源,Flink RM 向 YARN RM 申请资源),ResourceManager 分配 Container 资源后,由 ApplicationMaster 通知资源所在节点的 NodeManager 启动 TaskManager。
  5. NodeManager 加载 Flink 的 Jar 包和配置构建环境并启动 TaskManager,TaskManager 启动后,向 JobManager 发送心跳包,并等待 JobManager 向其分配任务。

任务调度原理

在这里插入图片描述

  • Client 为提交 Job 的客户端,可以运行在任何机器上(与 JobManager 环境连通即可)。提交 Job 后,Client 可以结束进程(Streaming 的任务),也可以不结束并等待结果返回。
  • JobManager 主要负责调度 Job 并协调 Task 做 checkpoint,职责上很像 Storm 的 Nimbus。从 Client 处接收到 Job 和 Jar 包等资源后,会生成优化后的执行计划,并以 Task 的单元调度到各个 TaskManager 去执行。
  • TaskManager 在启动的时候就设置好了槽位数,每个 Slot 能启动一个 Task,Task 为线程。从 JobManager 处接收需要部署的 Task,部署启动后,与自己的上游建立 Netty 连接,接收数据并处理。
  • 当 Flink 集群启动后,首先会启动一个 JobManger 和一个或多个的 TaskManager。由 Client 提交任务给 JobManager,JobManager 再调度任务到各个 TaskManager 去执行,然后 TaskManager 将心跳和统计信息汇报给 JobManager。TaskManager 之间以流的形式进行数据的传输。

并行度(Parallelism)

  • 一个特定算子的子任务(subtask)的个数被称之为其并行度。一般情况下,一个流程序的并行度,可以认为就是其所有算子中最大的并行度。
  • 需要设置的 Slot 的数量是并行度的最大值。如果 Slot 分组,则是每组 Slot 并行度的最大值之和。
    在这里插入图片描述

TaskManger 与 Slot

  • Flink 中每一个 TaskManager 都是一个JVM 进程,它可能会在独立的线程上执行一个或多个子任务。为了控制一个 TaskManager 能接收多少个 Task,TaskManager 通过 Task Slot 来进行控制。
  • 每个 Task Slot 表示 TaskManager 拥有资源的一个固定大小的子集。Slot 目前仅仅用来隔离 Task 的受管理的内存,不会涉及到 CPU 的隔离。
  • 同一个 SlotGroup 的算子能共享同一个 Slot,不同组则必须另外分配独立的 Slot。
    在这里插入图片描述
  • 默认情况下,Flink 允许子任务共享 Slot,即使它们是不同任务的子任务(前提是来自同一个 Job)。 这样的结果是,一个 Slot 可以保存作业的整个管道(pipeline)。
    在这里插入图片描述

Slot 与 Parallelism

  • Task Slot 是静态的概念,是指 TaskManager 具有的并发执行能力,可以通过参数 taskmanager.numberOfTaskSlots 进行配置。
  • Parallelism 是动态概念,即 TaskManager 运行程序时实际使用的并发能力,可以通过参数 parallelism.default 进行配置。
  • 假设一共有 3 个 TaskManager,每个 TaskManager 中的分配 3 个 Task Slot,一共 9 个 Task Slot。如果设置 parallelism.default = 1,即默认并行度为 1,只用了 1 个 Task Slot,有 8 个空闲。因此设置合适的并行度才能提高效率。

程序与数据流(DataFlow)

  • 所有的 Flink 程序都是由三部分组成的:Source、Transformation 和 Sink。Source 负责读取数据源,Transformation 利用各种算子进行处理加工,Sink 负责输出。
    在这里插入图片描述
  • 在运行时,Flink 上运行的程序会被映射成逻辑数据流(DataFlow)。DataFlow 类似于任意的有向无环图,以一个或多个 Source 开始,以一个或多个 Sink 结束。
  • 在大部分情况下,程序中的转换运算(Transformation)跟 DataFlow 中的算子(Operator)是一一对应的关系,但有时候,一个 Transformation 可能对应多个 Operator。
    在这里插入图片描述

执行图(ExecutionGraph)

  • 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 过程,类似于 Spark 中的 shuffle 过程。

任务链(Operator Chains)

  • 相同并行度的 one-to-one 操作(两个条件必须满足),Flink 这样相连的算子链接在一起形成一个 Task,原来的算子成为里面的一部分。
  • 将算子链接成 Task 是非常有效的优化:它能减少线程之间的切换和基于缓存区的数据交换,在减少时延的同时提升吞吐量。链接的行为可以在编程 API 中进行指定。
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值