今年 Flink 火的一塌糊涂,一些大厂比如阿里巴巴也都开始使用 Flink 构建实时数据仓库。
一、什么是 Flink ?
1.1 批处理和流处理
数据集分为有界数据集和无界数据集:
- 有界数据集:某个时间区间内(比如全表),批处理;
- 无界数据集:源源不断(比如日志),流处理。
注意:
流处理更复杂,因为需要考虑到数据的顺序错乱和系统容错等。
1.2 Flink
相对于传统的数据处理模式,流式数据处理
有着更高的处理效率和成本控制能力。Flink 就是近年来在开源社区不断发展的具有优越的性能。Flink 可以描述为:
- 核心是一个
流式的数据流执行引擎
; - 能够基于同一个 Flink 运行时,提供支持
流处理
和批处理
两种类型应用; - 支持
高吞吐、低延迟、高性能
的分布式
处理。
二、为什么使用 Flink ?
使用 Flink 的原因肯定是 Flink 较以往其他产品更有竞争力
,这种竞争力主要体现在高性能的流式计算能力
。要理清这种竞争力需要从数据架构的演变说起。
2.1 传统单体数据架构
传统单体数据架构中,数据集中存储,架构分成计算层和存储层。这种架构初期效率很高,但是随着 业务种类越来越多
,系统越来越难以维护升级
,此外 database 是唯一准确的数据源,每个 application 都需要访问 database 来获取对应的数据,一旦 database 发生改变或者出现问题,将会对整个业务系统产生影响
。
2.2 微服务架构
微服务架构的核心是:1个 application 由
多个小的且相互独立的微型服务
组成,各服务的开发和发布不存在依赖关系
,这样整个系统相比于之前的传统单体数据架构就更加灵活
。
2.3 大数据 lambada 架构
然而随着业务数据的迅速增长,传统的 RDBMS 已经不能够很好地支撑大规模数据集的存储和分析所需要的性能需求,越来越多企业开始寻求基于 Hadoop 构建其业绩大数据平台,比如数据湖。而且 Hive、Spark 等组件使得 SQL 在Hadoop 的应用变得简单而高效。
lambada 架构分两种处理途径,Speed layer 负责批处理(比如 Hadoop MapReduce),Batch layer 则负责流处理(比如 Storm)。这种架构存在问题:框架较多会导致平台复杂度高、运维成本高。虽说后面 Spark 框架能够同时支持批计算和流计算,但是Spark Streaming 的流计算本质上依旧是微批处理并非实时流计算。
2.4 Flink 优势
相比于 Spark Streaming的微批处理模式,Flink 通过 Google Dataflow 模型实现了实时流计算框架,将有界数据集转换成无界数据集统一进行流式处理
。Flink 具有如下优势:
-
高吞吐、低延时、高性能
; - 支持事件时间(Event Time)概念:大多数框架中
窗口计算
采用系统时间
(即Event 到达计算框架是 host 的当前时刻),而 Flink 则能够基于事件时间
(即 Event 本身产生的时刻,当然也可以基于其他类型)语义进行窗口计算。基于事件驱动的优势在于即使 Event 到达的顺序乱了,框架也能够准确知道事件的时序性
; - 支持高度灵活的窗口计算操作:流处理中,数据就像stream 一样源源不断地进入到框架中进行处理。有时需要通过窗口的方式对 stream 进行一定范围内的聚合计算。比如统计某网页在过去1分钟内的点击数。这种情况下就需要定义一个窗口,收集最近1分钟内的数据,并对这些数据进行计算。Flink 的窗口计算包括Time、Count、Session、Data-driven 等类型的窗口操作,可以灵活使用
触发条件定制化来达到复杂的计算需求
; - 基于轻量级分布式
快照(Snapshot)实现容错
机制:Flink 可以分布运行在多达上千个节点上,将一个大型计算任务流程分解成多个小的计算stage,再分布到节点上并行处理。任务执行过程中,分布式Snapshot(通过 Save points 实现) 的 Checkpoints能够将状态信息进行持久化到存储介质中(比如磁盘),一旦某些任务出现异常,就能够从Checkpoints 中恢复任务
,从而确保数据处理过程中的一致性; - 基于 JVM 实现独立的内存管理:大数据处理中内存管理是非常重要的部分,Flink 实现了
自身管理内存
的机制,且通过序列化/反序列化
方法将所有数据对象转换成二进制存储在内存中,降低数据存储 size 的同时,更高效地利用内存,降低 JVM GC 对框架性能的影响
。
三、Flink应用场景
- 实时智能推荐;
- 复杂事件处理;
- 实时欺诈检测;
- 实时数仓与 ETL;
- 流数据分析;
- 实时报表分析。
四、Flink 的架构
和大多数大数据处理框架类似,Flink 采用的也是主从架构(master/slaves)
,即1个 master 节点管理多个 (或1个)worker 节点,master 节点
上跑着 jobmanager
服务,worker 节点
上跑着 taskmanager
服务。客户端 client
则和 master 节点上的 JobManager 进行交互
。
4.1 架构概括
- JobManager(指挥者,JVM 进程):
协调
分布式执行安排任务
,协调检查点,协调故障恢复; - TaskManager(干活的工人,一个对应一个 JVM 进程):执行任务的数据流,缓冲器以及交换数据流;
- 客户端:提交任务、任务交互(提交任务后就可以退选择退出);
- 所有组件之间的
通信
借助于 Akka Framework,包括任务状态、Checkpoint 触发等消息。
注意:
- 可以通过
jps
或者ps -ef | grep java
命令来查看
Flink 进程; - 如果 Flink 集群配置为
HA
,那么将有多个 JobManager,其中一个是 Leader
,其余处于 Standby
。
4.2 常见术语 Job、task、subtask、slot、operator、dataflow、streams he transformation 操作
slot: 负责管理分配资源的单位
4.3 操作链和共享任务槽
Flink 默认有策略对 Job 进行 operator chain 和 slot sharing 的控制。
一个 TaskManager 是一个JVM 进程
,能够在不同线程中执行一个或者多个子任务。一个 worker 节点中包含至少一个任务槽 TaskSlot
,其就是用来控制一个 worker 接受多少个任务
。
任务槽的特点:
- Flink 的计算资源通过 TaskSlot 来定义,一个 TaskSlot 代表 TaskManager 中资源的固定子集;
- 同一个 worker 中的
每个 TaskSlot 分配的内存 memory 是相同的且隔离的
,但是CPU 却不是隔离的
; - 同一 JVM 中的
任务共享TCP连接(通过多路复用)和心跳消息
。 他们还可以共享数据集和数据结构
,从而减少每个任务的开销
。
任务槽共享的优点:
默认情况下,即使 subtasks 来自于不同 task 但是来自于同一个 job,则这些 subtasks 可以共享 slot。共享 slot 的好处:
- job 达到最高的并行度;
- 提高资源利用率。若 slot 不能共享,则哪些非密集型的任务(比如source、map)将会阻塞和密集的window subtask 一样多的资源。共享 slot 再提高并发度的同时也能够保证 subtask 之间合理的共享 slot;
-
一个约定俗成的规则是,task slot推荐的默认值是cpu的核数。对于超线程技术,每个slot占用两个或者更多的线程上下文。这样可能致使一个slot持有该job的整个pipeline。
操作链的特点:
- Operator Chains 就类似于 Spark 中的 pipeline,是指用户可以指定相应的链条将相关性很强的转换操作绑定在一起,这样就能够让上下游的 task 在同一个 pipeline 中执行;
- 每个 task 在一个线程中执行;
- 链接可以减少线程间的切换和数据缓冲的开销,并在降低延时的同时提高整体吞吐量;
-- 为什么?
- 操作链是默认开启的。
五、Flink 基本组件栈
Flink 架构分为3层,即上层的 API & Libaries、中层的 Runtime 核心层、物理部署层。
5.1 API & Libaries
从 API 结构上看上去,和 Spark 类似,同样包括批处理、流处理、机器学习、图计算,也同样提供 Java、Scala、Python 接口。
5.2 Runtime 核心层
为上层的 API 调用提供接触服务。支持分布式 Stream 作业的执行、JobGraph 和 ExecutionGraph 的映射转换、任务调度等,将 DataStream 和 DataSet 转成统一且可执行的 Task Operator,以同时实现批处理和流处理。
5.3 物理部署层
Flink 的部署模式,可以是local、集群(Standalone、YARN)、云端(GCE/EC2)、K8s(最近比较火)。
六、任务提交和处理流程
- Client 将 application(任务) 提交到 Flink cluster,并于 JobManager 创建 Akka 连接,然后将
application 提交给 JobManager
;提交方式包括:- CLI(类似 Spark-submit);
- Flink WebUI 提交;
- 应用程序中指定 JobManager 的 RPC 网络端口构建 Execution Environment 提交 Flink 应用;
- 根据 Flink 集群中 TaskManager 上 TaskSlot 的使用情况,为提交的 application `分配 TaskSlots 资源 并命令 TaskManager 启动 application;
- TaskManager 从 JobManager 接收计算任务,然后
使用 Slot 资源启动 application
,建立数据接入的网络连接、接收数据、处理数据;各 TaskManaer 之间的数据交互通过数据流进行; - JobManager 和 TaskManager 之间通过 Actor System 进行通信,application 的
执行进度会发送给 client 端
;在执行 application 过程中,JobManager 会触发 Checkpoints 操作
,每个 TaskManager 收到 Checkpoints 触发指令后,会完成 Checkpoint 操作; - application 执行完成后,执行状态将会反馈给 client 端,并
释放掉 TaskManager 中的资源
供下一次提交任务使用。
Flink 的任务运行采用的是多线程
方式,和 Spark 比较类似,和 Hadoop MapReduce 的多 JVM 进程的方式有较大区别,这使得 Flink 能够有效提高 CUP 使用效率,同时多个任务之间通过 TaskSlot 方式共享系统资源
,每个 TaskManager 管理多个 TaskSlot 资源池以有效管理资源。
七、时间和窗口计算
7.1 时间概念
时间属性:流式数据处理最大特点是数据具有时间属性,比如 event 的时间包括:事件生成时间 event
time、事件接入时间 ingestion time
、事件处理时间 process time
,如图所示:
- 终端生成事件排入消息中间件比如 kafka:event time;
- event 进入 flink 处理:ingestion time;
- 窗口函数算子计算任务:process time。
有时存在时间延迟等因素,可能会导致早生成的事件晚到 flink。Flink 默认采用 process time 事件概念,但是也可采用 event time 来处理事件以能够较好还原事件先后关系。
7.2 窗口计算
按照固定的时间或者长度将数据流(事件队列)切分成不同窗口
,然后对各窗口中的数据做聚合计算
,从而得到一定时间范围内的统计结果
。比如统计某网站最近5分钟内的点击量,点击的数据会源源不断以事件队列的形式进入到 Flink,通过每隔5分钟切割队列得到有界数据,并在窗口内对有界数据进行聚合计算就可以得到最近5分钟的点击数。
7.3 WaterMark 水位线
有时由于网络延迟等因素,会导致事件数据不能及时传输到 Flink 中。Flink 创建一个基于时间的窗口 Window 来处理该事件的所有数据,必须先等待该事件的所有数据都到达该窗口才能开始处理
,这时候就需要用到 WaterMark (表达数据到达完整性
),满足 WaterMark 条件就会触发相应的窗口计算
。
八、Flink 状态管理和容错机制
8.1 有状态计算
定义
:有状态计算是指,程序 存储
计算的中间结果
(缓存,比如 heap 内存或者 heap 外内存),并将其提供给后续 Function 或者 Operator 使用
。
8.1.1 有状态计算需求举例
- 用户要在 Stream 上实现机器学习的模型训练,有状态计算可以维护当前版本模型使用的参数;
- 用户要按照 minutes、hours、days 进行聚合计算,求去当前的 max、mean等聚合指标,有状态计算可以维护当前计算过程中产生的结果。
8.1.2 有状态计算的优势
- 极大地提升流式计算中数据使用范围和指标计算的复杂度;
- 不再需要借助于外部缓存(比如 Redis,会频繁地和外部系统交互,引起性能瓶颈)存储中间结果数据,提升性能和传输过程中的数据可靠性。
8.2 状态类型
区分依据:数据集 data set 是否按照 Key 进行分区,将状态分为:
- Keyed State:只能用于KeyedStream 类型数据集对应的 Functions 和 Operators;可以通过 Key Groups 进行管理,主要用于当算子 parallelism 发生改变时,自动重新分布 Keyed State 数据;
- Operator State:即 Non-keyed State,只和并行算子实例绑定,和数据元素中的 key 无关,每个算子持有部分数据;支持当算子实例 parallelism 发生变化时自动重新分配状态数据。
相同点:
keyed 和 Non-keyed 两种 state 都具有两种形式:
- Managed State(托管):
Flink Runtime
控制管理状态数据;推荐使用
,因为更好地数据状态重平衡
和内存管理
; - Row State(原生):
算子自己
管理状态数据。
8.3 检查点 Checkpoints
Flink 中使用 Checkpoints 来实现容错,保证数据一致性
。Savepoints
是一种特殊的 Checkpoints,底层基于 Checkpoints
实现。
8.3.1 Checkpoints 检查点机制
- 基于
异步轻量级的分布式 Snapshots
技术实现; - Snapshots 将同一时间点 Task/Operator 状态数据全局统一快照;
- Checkpoints Barrier:event 队列输入,Flink 间隔性地在队列中生成 barrier,将两个 barrier 之间的数据划分到各自的 checkpoint 中;当异常出现时,Operator 就能从上一次 Snapshot 中回复所有算子之前的状态;
- Checkpoints 数据通常存储在 HA 中,比如 JobManager 节点或者 HDFS。
8.3.2 Checkpoints 举例
比如在 KafkaConsumer 算子中维护 Offset 状态,当系统出现问题无法从 kafka 消费数据时,可以将 Offset 记录在 state 中,当任务重新恢复时,就能够从指定的 Offset 开始消费数据。
8.3.3 Checkpoints 开启方法
默认 Checkpoints 机制不开启
,可以在 application 代码中调用 enableCheckpointing(n)
来启用,其中 n 表示执行间隔(ms)。此外还有其他可配置参数:
- 设置模式:分
EXACTLY_ONCE
和AT_LEAST_ONCE
;- EXACTLY_ONCE:适合不许丢数据或重复数据,但性能较弱;-- 默认的
- AT_LEAST_ONCE:适合时延和吞吐量比较大,但数据一致性要求不高。
- Timeout 设置:超过设定的时间,将中断 Checkpoint 过程,默认10分钟,即 env.getCheckpointConfig().setCheckpointingTimeout(60000);
- 检查点之间最小时间间隔;
- 最大并行执行的检查点数量:默认1个;
- 设置外部检查点;
- failOnCheckpointigErrors:该参数决定 Checkpointing 中如果出现失败或者错误,任务是否被关闭,默认是 True。
env.enableCheckpointing(100);
// 设置模式
env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE)
8.4 保存点 Savepoints
save points 需要手动命令行触发,主要用于在升级和维护 Flink 集群过程中保存系统中的状态数据到指定存储介质中。
类别 | 区别 | 备注 |
---|---|---|
Checkpoints | 默认不开启,一旦开启系统自动触发完成 | 无 |
Savepoints | 用户手动命令行触发,系统升级维护中持久化状态数据 | 底层基于 Checkpoints |
8.4.1 Operator ID 配置
配置 Operator ID 的原因:
升级时,需要停止整个 Flink application,虽说通过 Savepoints 可以将状态数据持久化磁盘然后恢复任务,但是有可能用户 application 的代码可能做了修改,因此可能导致无法从磁盘中恢复状态数据,因此就需要一个具有唯一性的标记算子 ID。
- ID 可以自动生成(不推荐,不利于代码维护升级);
- ID 也可以由用户手工生成(使用 Operator 中的 uid 方法指定唯一 ID,将算子唯一区分出来)。
8.4.2 Savepoints 操作
- 手动触发 Savepoints
- 取消任务并触发 Savepoints
- 从 Savepoints 中恢复任务
- 释放 Savepoints 数据
- 指定 TargetDirectory 和 配置全局的 TargetDirectory
# cd 到 ${flink_home}/libexec/bin
cd /usr/local/Cellar/apache-flink/1.9.1/libexec/bin
# 触发,jobid 和 targetDirectory 分别是对应的job id 和 持久化路径
flink savepoint :jobid :targetDirectory
# 指定 yarn appid
flink savepoint :jobid :targetDirectory -yid :yarnappid
# 恢复任务
flink savepoint :jobid :savePointPath :runargs
# 取消任务并将中间结果持久化
flink cancel -s :targetDirectory :jobid
# 清除持久化数据
flink savepoint -d :savepointPath
# 配置默认的全局的 savepoints 持久化路径 -- 修改配置文件 flink-conf.yaml 中的配置项 state.savepoints.dir,比如
state.savepoints.dir: hdfs://flink/savepoints
8.5 状态管理器 StateBackend
StateBackend: 存储管理
Checkpoints 过程中的状态数据
。
8.5.1 StateBackend 类别
- MemoryStateBackend:基于内存,将全部状态数据存储在 JVM heap 内存中;
快速高效
,但是受限于内存容量,容易 OOM
; - FsStateBackend:基于文件系统(
本地或者 HDFS
),状态数据量大
,速度比较慢; - RocksDBStateBackend:第三方的 StateBackend,需要
引入依赖 jar
;异步方式对状态数据做 Snapshot,数据先写入 RockDB,然后异步写入本地或者 HDFS;适合数据量较大,性能介于 MemoryStateBackend 和 FsStateBackend 之间
。缺点是:采用 JNI(ava Native Interface) 方式进行数据交换,JNI 每次数据上限是 2^31 字节
,因此每次数据合并大小在 2^31 字节之内。
8.5.2 状态管理器配置
即指定 MemoryStateBackend、FsStateBackend、RocksDBStateBackend 中有谁来管理状态,默认是 MemoryStateBackend
。配置包括两个层面:
- application 级别:只对当前 application 生效;
- cluster 级别:对全局(所有 application)生效。
Application 级别:
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setStateBackend(new FsStateBackend("hdfs://namenode:40010/flink/checkpoints/"))
如果是 RocksDBStateBackend 则需要在 pom.xml 中引入maven 依赖 rockdb:
org.apache.flink
flink-statebackend-rocksdb_2.11
1.7.0
再将new FsStateBackend
修改为new RocksDBStateBackend
。
Cluster 级别:
修改配置文件 flink-conf.yaml 中的配置项 state.backend
和 state.checkpoint.dir
,其中前者指明 StateBackend 类型,后者指明状态数据持久化存储路径,比如:
state.backend: filesystem
state.checkpoints.dir: hdfs://nameode:40010/flink/checkpoints/
如果是 RocksDBStateBackend,则需要在 flink-conf.yaml 中添加配置项:
# 指定同时可以操作 RocksDBStateBackend 的线程数,默认1
state.backend.rocksdb.checkpoint.transfer.thread.num: 1
# 指定 RocksDB 存储的本地路径
state.backend.rocksdb.localdir: /varrockdb/flink/checkpoints
# 指定定时器服务的工厂类实现类,默认 HEAP,也可为 RocksDB
state.backend.rocksdb.timer-service.factory: HEAP
8.6 可查询状态 Querable State
算子的状态 Operator State 是 Flink 中的核心概念,Flink 中基于状态统计出来的结果数据必须输出到外部系统中才能被其他系统使用,通常业务系统无法直接与 Flink 对接并获取中间状态的结果数据。Flink 提供了状态查询服务,业务系统可通过 Restful API 直接查询 Flink 系统内部的状态数据。查询服务包括三个主要组件:
- QueryableStateClient:客户端,定义在用户 application 中,发出查询请求;
- QueryableStateProxy:代理服务,接受处理 QueryableStateClient 发出的请求,每个 TaskManager 中包含一个 QueryableStateProxy,向 QuerybleStateServer 发出请求,将请求结果返回给 QueryableStateClient;
- QuerybleStateServer:接受 QueryableStateProxy 的请求,每个 TaskManager 中运行一个 QuerybleStateServer,通过从本地的状态后台管理器中查询状态结果,并返回给 QueryableStateProxy。
九、Flink 的部署模式和高可用配置
9.1 Flink 的部署模式
Fink 部署模式包括多种,比较常见的是:Standalone 集群模式、YARN 集群模式、K8s 集群模式,其中:
- YARN 集群模式是指将 Flink application 提交给 YARN 来处理,包括两种:
YARN session 模式
、Single job 模式
,前者会在 YARN 上启动长时间运行的Flink session 集群
且用户可通过RestAPI 或 Web
页面提交 application 到 Flink session 集群上运行;后者则和大多数计算框架类似,每个 Flink 任务单独向 YARN 提交一个 application,且每个任务都有自己的 JobManager 和 TaskManager
(就像是 Spark 的 master 和 worker); - K8s 集群模式:用户可以基于 Kubernetes 部署 Flink Session 集群,可以通过 Docker 镜像方式向 K8s 集群提交独立的 Flink 任务。
9.2 Flink 的高可用
Flink 的高可用:主要强调的是JobManager 的高可用保证
,因为 JobManager 作为 Flink 集群的管理节点没负责整个集群的任务调度和资源管理
,默认不开启
高可用。目前高可用仅支持Standalone 集群
和YARN Session 集群
。
类别 | 方式 | 备注 |
---|---|---|
Standalone | JobManager 服务信息注册在 Zookeeper 中,通过 Zookeeper 完成 JobManager Leader 的选举,多个 JobManager 只有一个处于 active,其他处于 standby | 如果没安装 Zookeeper 集群,Flink 也自带了 Zookeeper |
YARN Session | 重启 JobManager 来保证高可用 | Flink JobManager 执行在 ApplicationMaster 所在容器中 |
十、Flink historyserver
Flink有一个历史记录服务器,可用于在关闭相应的Flink群集后查询已完成作业的统计信息
。此外,它公开了一个REST API
,它接受HTTP请求
并使用JSON
数据进行响应。
参考:
https://www.cnblogs.com/ronnieyuan/p/11853287.html
https://www.jianshu.com/p/0cd1db4282be
https://blog.csdn.net/zero__007/article/details/88201498