flink
1、概述
flink是一个计算框架和分布式处理引擎,用于对有界流和无界流进行有状态计算
-
dataset API
- 对静态数据进行批处理操作,将静态数据抽象成分布式的数据集,用户可以方便地使用Flink提供的各种操作符对分布式数据集进行处理,支持Java、Scala和Python1
-
datastream api
- 对数据流进行流处理操作,将流式的数据抽象成分布式的数据流,用户可以方便地对分布式数据流进行各种操作,支持Java和Scala
-
table api
- 对结构化数据进行查询操作,将结构化数据抽象成关系表,并通过类SQL的DSL对关系表进行各种查询操作,支持Java和Scala
2、优点
1、支持高吞吐、低延迟、高性能的流处理
2、支持带有事件时间的窗口(Window)操作
3、支持有状态计算的Exactly-once语义
4、支持高度灵活的窗口(Window)操作,支持基于time、count、session,以及data-driven的窗口操作
-
八种窗口
- 时间滑动滚动
- 会话滑动滚动
- count滑动滚动
5、支持具有反压功能的持续流模型
6、支持基于轻量级分布式快照(Snapshot)实现的容错
7、一个运行时同时支持Batch on Streaming处理和Streaming处理
8、Flink在JVM内部实现了自己的内存管理,避免了出现oom
9、支持迭代计算
10、支持程序自动优化:避免特定情况下Shuffle、排序等昂贵操作,中间结果有必要进行缓存
3、系统架构
JobManager
-
master进程,负责job的管理和资源调度,包括任务调度、检查点管理、失败恢复等,也支持HA,多个master进程,一个作为leader,其余的是standby,当leader失败时,会选出一个standby的master作为新的leader(通过zookeeper实现leader选举)。
-
actor系统
Flink内部使用Akka模型作为JobManager和TaskManager之间的通信机制。
Actor系统是个容器,包含许多不同的Actor,这些Actor扮演者不同的角色。Actor系统提供类似于调度、配置、日志等服务,同时包含了所有actors初始化时的线程池。
所有的Actors存在着层级的关系。新加入的Actor会被分配一个父类的Actor。Actors之间的通信采用一个消息系统,每个Actor都有一个“邮箱”,用于读取消息。如果Actors是本地的,则消息在共享内存中共享;如果Actors是远程的,则消息通过RPC远程调用。每个父类的Actor都负责监控其子类Actor,当子类Actor出现错误时,自己先尝试重启并修复错误;如果子类Actor不能修复,则将问题升级并由父类Actor处理。
在Flink中,actor是一个有状态和行为的容器。Actor的线程持续的处理从“邮箱”中接收到的消息。Actor中的状态和行为则由收到的消息决定。
-
调度器
Flink中的Executors被定义为task slots(线程槽位)。每个Task Manager需要管理一个或多个task slots。
Flink通过SlotSharingGroup和CoLocationGroup来决定哪些task需要被共享,哪些task需要被单独的slot使用。
-
检查点
Flink的检查点机制是保证其一致性容错功能的骨架。它持续的为分布式的数据流和有状态的operator生成一致性的快照。其改良自Chandy-Lamport算法,叫做ABS(轻量级异步Barrier快照),具体参见论文:
Lightweight Asynchronous Snapshots for Distributed DataflowsFlink的容错机制持续的构建轻量级的分布式快照,因此负载非常低。通常这些有状态的快照都被放在HDFS中存储(state backend)。程序一旦失败,Flink将停止executor并从最近的完成了的检查点开始恢复(依赖可重发的数据源+快照)。
Barrier作为一种Event,是Flink快照中最主要的元素。它会随着data record一起被注入到流数据中,而且不会超越data record。每个barrier都有一个唯一的ID,将data record分到不同的检查点的范围中
每个快照中的状态都会报告给Job Manager的检查点协调器;快照发生时,flink会在某些有状态的operator上对data record进行对齐操作(alignment),目的是避免失败恢复时重复消费数据。这个过程也是exactly once的保证。通常对齐操作的时间仅是毫秒级的。但是对于某些极端的应用,在每个operator上产生的毫秒级延迟也不能允许的话,则可以选择降级到at least once,即跳过对齐操作,当失败恢复时可能发生重复消费数据的情况。Flink默认采用exactly once意义的处理。
-
TaskManager
- Task Managers是具体执行tasks的worker节点,执行发生在一个JVM中的一个或多个线程中。Task的并行度是由运行在Task Manager中的task slots的数量决定。如果一个Task Manager有4个slots,那么JVM的内存将分配给每个task slot 25%的内存。一个Task slot中可以运行1个或多个线程,同一个slot中的线程又可以共享相同的JVM。在相同的JVM中的tasks,会共享TCP连接和心跳消息
client
-
Job Client并不是Flink程序执行中的内部组件,而是程序执行的入口。Job Client负责接收用户提交的程序,并创建一个data flow,然后将生成的data flow提交给Job Manager。一旦执行完成,Job Client将返回给用户结果。
- Data flow就是执行计划,当用户将程序提交时,Job Client负责接收此程序,并根据operator生成一个data flow。默认情况下,Flink的data flow都是分布式并行处理的,对于数据的并行处理,flink将operators和数据流进行partition。Operator partitions叫做sub-tasks。数据流又可以分为一对一的传输与重分布的情况。
4、Flink 原理与实现:数据流上的类型和操作,Datastream为主
DataStream 是 Flink 流处理 API 中最核心的数据结构。它代表了一个运行在多个分区上的并行流。一个 DataStream 可以从 StreamExecutionEnvironment 通过env.addSource(SourceFunction) 获得。
DataStream 上的转换操作都是逐条的,比如 map(),flatMap(),filter()。DataStream 也可以执行 rebalance(再平衡,用来减轻数据倾斜)和 broadcaseted(广播)等分区转换。
val stream: DataStream[MyType] = env.addSource(new FlinkKafkaConsumer08String)
val str1: DataStream[(String, MyType)] = stream.flatMap { … }
val str2: DataStream[(String, MyType)] = stream.rebalance()
val str3: DataStream[AnotherType] = stream.map { … }
上述 DataStream 上的转换在运行时会转换成如下的执行图:

如上图的执行图所示,DataStream 各个算子会并行运行,算子之间是数据流分区。如 Source 的第一个并行实例(S1)和 flatMap() 的第一个并行实例(m1)之间就是一个数据流分区。而在 flatMap() 和 map() 之间由于加了 rebalance(),它们之间的数据流分区就有3个子分区(m1的数据流向3个map()实例)。这与 Apache Kafka 是很类似的,把流想象成 Kafka Topic,而一个流分区就表示一个 Topic Partition,流的目标并行算子实例就是 Kafka Consumers。
http://wuchong.me/blog/2016/05/20/flink-internals-streams-and-operations-on-streams
5、实时计算
资源申请
- 粗粒度资源申请
- 和spark一样
任务调度
- spark streaming每一个batch做一次任务调度
- flink 只做一次任务调度,在启动开的时候,task只部署一次
- 这是flink比spark快的重要原因
6、程序结构
source
-
整个stream的入口
-
1、基于本地集合
-
2、基于文件
-
3、读取socket
-
4、自定义source
-
实现sourcefunction接口
- SourceFunction 单线程
- richsourcefunction 多了open 方法,可以在open 方法中打开链接,使用flink的状态
- ParallelSourceFunction 并行source
- RichParallelSourceFunction
-
读取kafka中的数据
- flinkkafkaconsumer
-
自定义读取mysql
-
自定义读取hbase
-
-
transformation
- map
- filter
- flatMap
- keyBy
- sum
- max maxBy min MinBy
- window
- join (难点)
- union
sink
-
1、打印
-
2、写入文件
-
3、写入socket
-
4、自定义,实现sinkFunction 或者rickSinkFunction
-
sink到kafka
- TwoPhaseCommitSinkFunction 两步提交的sink
-
7、集群搭建
独立集群
- 和hadoop没有关系,独立搭建一个flink集群,
fink on yarn
-
yarn -session
- 在yarn 上启动一个jobmanager 所有job共享一个jobmanager
-
per-job cluster
- 直接提交任务到yarn集群,每一个job独享一个jobmanager
提交方式
- 1、通过网页提交
- 2、通过命令好提交 flink run
- 3、rpc
8、时间
1、事件时间
-
数据中必须自带时间才可以使用
-
数据乱序问题
- 通过将水位线后移解决乱序问题
2、接收时间
3、处理时间 默认
9、窗口
时间窗口
- 事件时间的滚动窗口
- 事件时间的滑动窗口
- 处理时间的滚动窗口
- 处理时间的滑动窗口
会话窗口
- 每隔一段时间没有数据进行计算
- 事件时间和处理时间
统计窗口
- 滚动和滑动
10、状态
flink可以将程序中间计算结果以状态的形式保存起来 (单词的数量,消费偏移量)
状态会随着checkpoint保存到hdfs中,保证数据不丢失
分类
- valueState
- listState
- ReduingState
- MapState
针对每一个key都会保存一个状态
11、checkpoint
分布式快照,将状态持久化到hdfs中
流程
- 1、jobmanager定时发送checkpoint, 向sourcetask发送触发器
- 2、sourcetask会在数据中安插标记,同时自己进行checkpiint 将数据保存到hdfs中
- 3、下游算子收到标记后异步进行快照
- 4、当所有的算子处理完同一个标记后代表一次checkpoint成功
状态后端
-
MemoryStateBackend
- 将状态保存到jobmanager的内存中
-
FsStateBackend
- 首先将状态保存在taskmamager的内存中,
- 当进行checkpoint的时候将状态保存到hdfs中
-
RocksDBStateBackend
- 首先将状态保存在taskmanager本地的RocksDS中
- 当进行checkpoint的时候将状态保存到hdfs中
- 可以支持增量快照
12、table api
基于flink runtime层构建的sql 层 ,将批处理和流处理统一,将批处理看作是流处理的特例
读取数据处理数据都可以通过sql 来实现
动态表
-
flink 实时数据流 在flink 的代码中抽象成一张表
-
基于动态表可以进行连续查询(查询不结束,一致会产生结果)
-
根据查询的不同又分为两种查询结果
-
append 流
- 可以写入任何接收器中
-
更新 流
- 可以写入mysql
- 如果想要写入kafka 需要使用阿里云提供的 changelog-json 在flink 1.13之后可以直接写入kafka
-
flinK cdc
-
mysql -cdc
- 先进行批量读取
- 再实时监控binlog日志,实时读取数据
- 一般用在维表关联
表关联
-
维表关联
-
维表再mysql中使用jdbc读取mysql数据进行关联
- 如果维表数据更新了,再flink中没办法发现
-
维表再mysql中,使用mysql-cdc 读取数据,可以实时发现数据更新
-
-
Regular Joins (全局管理)
- 需要将两个表所有的数据保存再flink的状态中
- 当程序运行很久之后状态会很大,checkpoint的时间会很长
-
Interval Joins
- 指定一个时间范围进行关联
- a 表关联b 表一段时间内的数据 ,状态中不需要保存所有数据
sql-client
- 再sql客户端中可以直接写sql, 可以创建表,查询数据
- 默认元数据是放在内存中的,可以整合hive将元数据保存到hive中
- flink 可以直接读取hive中的表进行查询
- hive不可以使用flink创建表动态表
13、Exactly once
1、消息生产者
-
1、acks
- acks=0 生产者发送出去就代表成功
- acks=1 当分区的leader接收到数据并保存到磁盘上代表成功
- acks=all 当分区的所有副本都保存成功才代表成功
-
2、事务
- 多次发送数据要么都成功要么都失败
-
3、幂等性
- 如果在发送数据的时候超时重新发送不会出现重复数据
2、flink消费端
- 通过flink的状态和checkpoint保存消费的偏移量以及计算的中结果,保证数据不重复
3、flink生产端
- 1、依赖flink两步提交的sink
- 2、依赖kafka事务支持
- 在checkpoint之前开启事务
- 在checkpoint成功之后提交i事务
- 会导致数据输出延迟