Flink
概述
Apache Flink 是一个框架和分布式处理引擎,用于在无边界和有边界数据流上进行有状态的计算。Flink 能在所有常见集群环境中运行,并能以内存速度和任意规模进行计算
无界流
-
有定义流的开始,但没有定义流的结束
- 持续产生数据
- 数据需要持续处理+立刻处理
- 以特定的顺序摄取事件
- 流处理
-
就是流数据
有界流
-
有定义流的开始, 也有定义流的结束
- 可以摄取所有数据后再进行计算
- 所有数据可以被排序, 不需要有序摄取
- 批处理
-
就是静态数据
Apache Flink 擅长处理无界和有界数据集
- 时间控制
- 状态化
部署应用到任意地方
-
Flink 集成了所有常见的集群资源管理器
- Hadoop YARN
- Apache Mesos
- Kubernetes
-
通过资源管理器特定(resource-manager-specific)的部署模式实现
-
提交或控制应用程序的所有通信都是通过 REST 调用进行的
-
REST
- REST代表表现层状态转移(representational state transfer)
- 在设计API时,使用路径定位资源,方法定义操作,通过Content-Type和Accept来协商资源的类型
- 就是一种调用规范
-
运行任意规模应用
- 处理每天处理数万亿的事件
- 应用维护几TB大小的状态
- 应用在数千个内核上运行
利用内存性能
- 任务状态始终保留在内存中, 如果状态大小超过内存, 那么会保存在高效访问的磁盘数据结构中
- Flink 通过定期和异步地对本地状态进行持久化存储来保证故障场景下精确一次的状态一致性。
API 和库
流处理应用的基本组件
-
框架控制流,状态和时间的性能----决定---->流处理框架----决定---->应用程序的构建和执行
-
流
- 基本要素
- 任何类型数据流
-
状态(即n个中间结果, 可能对应的数据结构会不同)
-
每一个具有一定复杂度的流处理应用都有状态
- 与特定算子相关联
- 不可夸算子读取状态
-
应用状态管理相关的特性支持
-
多种状态基础类型
-
根据数据结构的不同, 对应的状态基础类型也不同
- 原子值---->value
- 列表---->list
- 映射---->map
-
checkpoint检查点
- 有状态流应用的一致检查点,其实就是所有任务的状态,在某个时间点的一份拷贝(一份快照);这个时间点,应该是所有任务都恰好处理完一个相同的输入数据的时候
-
-
精确一次语义
- checkpoint 和故障恢复算法保证了故障发生后应用状态的一致性
- Flink 能够在应用程序发生故障时,对应用程序透明,不造成正确性的影响
-
超大数据量状态
-
可弹性伸缩的应用
- 可增可减
- 支持有状态应用的分布式横向伸缩
-
-
关于状态的容错的机制
-
Raw
- 原生的, 自己编写
-
Manage
- Flink的一套完整机制
-
-
注册状态
-
算子状态(不常用)
-
只有当前算子可以访问
- 只要在同一个分区, 算子状态是相同的
-
数据结构
-
列表状态
- 将状态存储为一个列表, 重分配时由原状态存储机进行分配(效率高)
-
联合列表状态
- 也是一个列表, 但是由新增后的机器选取状态列表(效率低)
-
广播状态
- 一个算子的多个并行子任务, 如果要求各个子任务状态相同, 那么就需要这种
-
-
-
键控状态Keyed State
-
只有当前key可以访问
-
每一个Key维持一个状态
-
类型
- 值状态
- 列表状态
- 映射状态
- 聚合状态
-
-
状态后端
-
用于状态的存储,访问,维护的组件
-
可插入组件
-
任务
- 管理本地状态
- 管理checkpoint
-
类型
-
MemoryStateBackend
-
内存级的状态后端
-
将键控状态作为内存中的对象进行管理
- 存储在TaskManager的JVM堆
-
checkpoint
- 存储在JobManager的内存中
-
特点
-
低延迟
-
不稳定
- 假如JobManager挂了…
-
-
-
FsStateBackend(默认)
-
本地状态
- 存在TaskManager的JVM堆
-
checkpoint
-
存到远程的持久化文件系统(FileSystem)上
- HDFS…
-
-
速度快, 并且可扩展
-
-
RocksDBStateBackend
- 将所有状态序列化后,存入本地的RocksDB中存储
- RockDB是一个Facebook的数据库
- 需要访问硬盘, 速度会慢, 但是稳定
-
不同的Job可以分别灵活设置自己的状态存储策略
-
重启策略
-
region
- 只重启挂掉的机器
-
-
-
-
-
-
时间
-
流处理的重要组成部分
-
大多数事件流都有事件本身固有的时间语义
-
例如
- 窗口聚合
- 会话计算
- 模式检测
- 基于时间的join
-
-
衡量时间
- 事件时间(event-time)
- 处理时间(processiong-time)
-
时间相关的语义支持
-
事件时间模式
-
根据事件本身自带的时间戳进行结果的计算
- 所以, 无论是历史记录还是实时处理, 结果都一样
-
-
处理时间模式
-
根据处理引擎的机器时钟触发计算
- 低延迟
- 小误差
-
-
Watermark(水印) 支持
-
用于衡量事件时间进展
-
好处
- 平衡了处理延时(效率)和完整性(可用)
-
-
迟到数据处理
-
迟到事件
- 当以带有 watermark 的事件时间模式处理数据流时,在计算完成之后仍会有相关数据到达。
-
多种处理机制
- 将这些数据重定向到旁路输出(side output)
- 更新之前完成计算的结果
-
-
-
-
数据管道 & ETL(抽取、转换、加载)
-
无状态的转换
-
map()
-
适用于一对一的转换
- 对每个进入算子的流元素,map() 将仅输出一个转换后的元素。
-
-
flatmap()
- 使用接口中提供的 Collector ,flatmap() 可以输出你想要的任意数量的元素,也可以一个都不发。
- 一变多(打散)
-
Filter()
- 过滤
-
-
Keyed Streams
- keyBy()
rides
.flatMap(new NYCEnrichment())
.keyBy(enrichedRide -> enrichedRide.startCell)
-
使所有具有相同属性的事件分到相同的组里
-
每个 keyBy 会通过 shuffle 来为数据流进行重新分区。总体来说这个开销是很大的,它涉及网络通信、序列化和反序列化
- 通过计算得到键
keyBy(ride -> GeoUtils.mapToGridCell(ride.startLon, ride.startLat))
-
Keyed Stream 的聚合
-
(隐式的)状态
-
考虑状态的大小
- 如果键值的数量是无限的,那 Flink 的状态需要的空间也同样是无限的。
- 在流处理场景中,考虑有限窗口的聚合往往比整个流聚合更有意义
-
-
reduce() 和其他聚合算子
-
reduce() 函数
- 实现自定义聚合
-
-
有状态的转换
-
为什么要状态管理?
- 本地性: Flink 状态是存储在使用它的机器本地的,并且可以以内存访问速度来获取
- 持久性: Flink 状态是容错的,例如,它可以自动按一定的时间间隔产生 checkpoint,并且在任务失败后进行恢复
- 纵向可扩展性: Flink 状态可以存储在集成的 RocksDB 实例中,这种方式下可以通过增加本地磁盘来扩展空间
- 横向可扩展性: Flink 状态可以随着集群的扩缩容重新分布
- 可查询性: Flink 状态可以通过使用 状态查询 API 从外部进行查询。
-
Rich Functions
-
FilterFunction, MapFunction,和 FlatMapFunction
- 单一抽象方法模式
-
“rich” 的变体
-
增加了以下方法
-
open(Configuration c)
- 算子初始化时调用一次
- 可以用来加载一些静态数据,或者建立外部服务的链接等
-
close()
-
getRuntimeContext()
- 创建和访问 Flink 状态的途径
-
-
-
-
清理状态
- keyHasBeenSeen.clear()
-
Non-keyed State
-
-
Connected Streams
-
更灵活地调整转换的某些功能
- 比如数据流的阈值、规则或者其他参数
-
一个单独的算子有两个输入流
-
实例
-
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStream<String> control = env
.fromElements("DROP", "IGNORE")
.keyBy(x -> x);
DataStream<String> streamOfWords = env
.fromElements("Apache", "DROP", "Flink", "IGNORE")
.keyBy(x -> x);
control
.connect(streamOfWords)
.flatMap(new ControlFunction())
.print();
env.execute();
}
-
注意两个流只有键一致的时候才能连接
-
两个流中所有键相同的事件发到同一个实例上
分层API
-
分层依据: 抽象程度
-
ProcessFunction(events,state,time)
-
ProcessFunction 可以处理一或两条输入数据流中的单个事件或者归入一个特定窗口内的多个事件
-
细粒度
-
状态
-
状态可以保存, 用于后续程序读取前面已发生的状态, 进行相应的处理
-
-
最基础的状态类型
-
能够为被其封装的变量添加容错能力的类型
-
一种 keyed state
- 只能用于keyBy的DataStream
-
-
-
-
-
DataStream API
-
提供通用的流操作的处理原语
- 窗口/逐条记录的转换
- 外部数据库查询
- 等等
-
Java/Scala
-
已定义的函数
- reduce()
- aggregate()
- map()
- 等等
-
-
SQL & Table API
- 在无边界的实时数据流和有边界的历史记录数据流上,关系型 API 会以相同的语义执行查询, 并产生相同的结果
- Table API 和 SQL 借助了 Apache Calcite 来进行查询的解析,校验以及优化。它们可以与 DataStream 和 DataSet API 无缝集成,并支持用户自定义的标量函数,聚合函数以及表值函数
-
库
-
Flink 具有数个适用于常见数据处理应用场景的扩展库
- 可扩展
- 非完全独立(包含其他API)
-
类型
-
复杂事件处理(CEP)
-
模式检测
-
应用范围
- 网络入侵检测
- 业务流程监控
- 欺诈检测
-
-
Data Set API
-
用于批处理
-
基础算子
- map、reduce、(outer) join、co-group、iterate等
-
算法
- 混合散列连接(hybrid hash-join)
- 外部归并排序(external merge-sort)
-
-
Gelly
-
可扩展的图形处理和分析库
-
实现并集成在DataSet API之上
-
内置算法
- label propagation、triangle enumeration 和 page rank 算法
- Graph API
-
-
运维
7 * 24小时稳定运行
-
故障时重启
-
故障时保证能够持久化服务内部各个组件状态状态
- 状态持久化
-
可持续性与一致性维护机制
-
检查点的一致性
- 应用服务会重启后,再重新加载上一次成功备份的状态检查点信息
- 保证精确一次(exactly-once)的状态一致性
-
高效的检查点
-
Flink采用异步及增量的方式构建检查点服务
- 解决状态信息数据量过大以及延迟高的问题
-
-
端到端的精确一次
- 事务型输出
-
集成多种集群管理服务
- Hadoop YARN, Mesos, 以及 Kubernetes
-
内置高可用服务
-
解决单点故障区问题
- 基于ZooKeeper
-
-
Flink能够更方便地升级、迁移、暂停、恢复应用服务
-
Savepoint保存点(快照)
-
和checkpoint相似, 但是savepoint需要手动启动, 并且当流应用服务停止时, 不会自动删除
-
用处
- Savepoint 常被应用于启动一个已含有状态的流服务,并初始化其(备份时)状态
- 就是初始化状态(类似一键安装的东西)
-
特点
- 便于升级应用服务版本
- 方便集群服务移植
- 方便Flink版本升级
- 增加应用并行服务的扩展性
- 便于A/B测试及假性分析场景对比结果
- 暂停/恢复服务
- 归档服务
-
监控和控制应用服务
-
Flink与许多常见的日志记录和监视服务集成得很好,并提供了一个REST API来控制应用服务和查询应用信息
-
Web UI方式
- Flink提供了一个web UI来观察、监视和调试正在运行的应用服务。并且还可以执行或取消组件或任务的执行。
-
日志集成服务
- Flink实现了流行的slf4j日志接口,并与日志框架log4j或logback集成
-
指标服务
- Flink提供了一个复杂的度量系统来收集和报告系统和用户定义的度量指标信息。度量信息可以导出到多个报表组件服务,包括 JMX, Ganglia, Graphite, Prometheus, StatsD, Datadog, 和 Slf4j
-
标准的WEB REST API接口服务
- Flink提供多种REST API接口,有提交新应用程序、获取正在运行的应用程序的Savepoint服务信息、取消应用服务等接口。REST API还提供元数据信息和已采集的运行中或完成后的应用服务的指标信息。
-
应用场景
事件驱动型应用
-
概念
-
事件驱动型应用是一类具有状态的应用,它从一个或多个事件流提取数据,并根据到来的事件触发计算、状态更新或其他外部动作。
- 通俗说: 当发生了XXX事件时进行XXX的动作的应用
-
-
应用从本地(内存/磁盘)访问获取数据
- 数据和计算不分离
-
容错性
- 定期向远程持久化存储写入checkpoint
-
优势
-
无须查询远程数据库
-
更高的吞吐和更低的延迟
-
只需考虑自身数据
- 协调工作将大大减少
-
-
Flink 如何支持事件驱动型应用?
-
一系列丰富的状态操作原语
-
ProcessFunction
- 细粒度时间控制,方便实现一些高级业务逻辑
-
复杂事件处理(CEP)类库
- 用来检测数据流中的模式
-
-
savepoint
- 用来初始化任意状态兼容的应用
- 在完成一次 savepoint 后,即可放心对应用升级或扩容,还可以启动多个版本的应用来完成 A/B 测试
-
数据分析应用
-
概念
-
数据分析任务需要从原始数据中提取有价值的信息和指标。
-
实时地进行数据分析
- 随着事件消费持续产生和更新结果
-
结果数据可能会写入外部数据库系统或以内部状态的形式维护
-
-
优势
-
省掉了周期性的数据导入和查询过程
-
获取指标的延迟更低
-
简化应用抽象
- 流式分析应用整体运行在 Flink 之类的高端流处理系统之上,涵盖了从数据接入到连续结果计算的所有步骤,因此可以依赖底层引擎提供的故障恢复机制
-
-
-
Flink 如何支持数据分析类应用?
-
SQL 接口
-
将批、流查询的语义统一起来
- 查询结果一样
-
-
支持丰富的用户自定义函数
- 允许在 SQL 中执行定制化代码
-
利用 Flink DataStream API 和 DataSet API 进行更低层次的控制
-
Gelly 库
-
数据管道应用
-
概念
-
提取-转换-加载(ETL)是一种在存储系统之间进行数据转换和迁移的常用方法
-
ETL 作业通常会周期性地触发,将数据从事务型数据库拷贝到分析型数据库或数据仓库
-
以持续流模式运行
- 支持从一个不断生成数据的源头读取记录,并将它们以低延迟移动到终点
-
-
优势
- 可以明显降低将数据移动到目的端的延迟
- 能够持续消费和发送数据
-
Flink 如何支持数据管道应用?
-
Flink 的 SQL 接口
-
Table API
-
用户自定义函数
-
DataStream API
-
Flink 为多种数据存储系统内置了连接器
- 如:Kafka、Kinesis、Elasticsearch、JDBC数据库系统等)
-
提供了文件系统的连续型数据源及数据汇,可用来监控目录变化和以时间分区的方式写入文件
-
实践
目标
- 实现可扩展并行度的ETL,数据分析,事件驱动的流式应用程序
- 如何实现流数据处理管道(pipelines)
- Flink 如何管理状态以及为何需要管理状态
- 如何使用事件时间(event time)来一致并准确地进行计算分析
- 如何在源源不断的数据流上构建事件驱动的应用程序
- Flink 如何提供具有精确一次(exactly-once)计算语义的可容错、有状态流处理
流处理
-
应用程序(就是一堆处理方法, 简称算子)
-
由用户自定义算子(operator, DAG途中每块都是一个算子)转换而来的流式dataflows组成
-
这些流式 dataflows 形成了有向图,以一个或多个源(source)开始,并以一个或多个汇(sink下沉)结束
-
并行Dataflows
- 算子子任务数就是对应算子的并行度
-
算子之间传输数据
-
一对一模式
- 例如上图中的 Source 和 map() 算子之间
- map() 算子的 subtask[1] 输入的数据以及其顺序与 Source 算子的 subtask[1] 输出的数据和顺序完全相同,即同一分区的数据只会进入到下游算子的同一分区
-
重新分发模式
-
例如上图中的 map() 和 keyBy/window 之间,以及 keyBy/window 和 Sink 之间
-
当你在程序中选择使用不同的 transformation,每个算子子任务也会根据不同的 transformation 将数据发送到不同的目标子任务
- keyBy()(通过散列键重新分区)、broadcast()(广播)或 rebalance()(随机重新分发)
-
重新分发数据的过程中,元素只有在每对输出和输入子任务之间才能保留其之间的顺序信息
- 例如,keyBy/window 的 subtask[2] 接收到的 map() 的 subtask[1] 中的元素都是有序的
-
不同键(key)的聚合结果到达 Sink 的顺序是不确定的
-
-
-
-
自定义时间流处理
- 使用记录在数据流中的事件时间的时间戳,而不是处理数据的机器时钟的时间戳
-
有状态流处理
-
应用场景
- 统计仪表板上每分钟显示的数据
- 训练作弊检测模型
-
有状态算子的并行实例组在存储其对应状态时通常是按照键(key)进行分片存储的
- 每个并行实例算子负责处理一组特定键的事件数据,并且这组键对应的状态会保存在本地
-
存储位置
-
状态访问在本地执行
- JVM堆
- 高速磁盘(以结构化数据格式)
-
-
实例
-
前三个算子的并行度为 2,最后一个 sink 算子的并行度为 1,其中第三个算子是有状态的
-
第二个算子和第三个算子之间是全互联的(fully-connected)
- 通过网络进行数据分发
- 根据某些键对数据分区, 方便各个部分的状态记录, 每个部分统一计算处理
-
-
-
通过状态快照实现的容错
-
通过状态快照和流重放两种方式的组合,Flink 能够提供可容错的,精确一次计算的语义。
-
状态快照在执行时会获取并存储分布式 pipeline 中整体的状态
- 它会将数据源中消费数据的偏移量记录下来,并将整个 job graph 中算子获取到该数据(记录的偏移量对应的数据)时的状态记录并存储下来
-