其他分享
首页 > 其他分享> > Flink总结

Flink总结

作者:互联网

Flink总结

从头儿过一遍书,做了些摘要。SQL那里还没仔细复习。

  1. 核心目标:数据流上的有状态计算

  2. 具体定位:以内存执行速度(速度快)和任意规模来执行计算(可扩展性强) -> 小松鼠快速灵巧

  3. 有状态的流处理可用于许多不同场景:

    • 事件驱动型应用:以Kafka为代表的消息队列几乎都是事件驱动型应用。因为有状态,不再需要查询数据库,而是本地访问数据。这样在吞吐量和延迟上可以有更好的性能。
      • 检查点可以异步增量地完成,对正常计算影响非常小。
    • 实时数据分析:利用状态
    • 数据管道:转换或扩展数据,在存储系统之间移动数据。

特点:

  1. 高吞吐和低延迟。
  2. 结果准确,乱序事件流,事件时间语义仍可以提供一致且准确的结果。
  3. 精确一次的状态一致性。
  4. 高可用。
  5. 可以连接到常用存储系统。

比较:

  1. 数据模型不同。
  2. 计算逻辑:Spark做批计算,需要将任务对应的DAG划分阶段,一个完成后经过shuffle再进行下一阶段的计算。Flink是标准的流式执行模式,一个事件在一个节点处理完后可以直接发往下一个节点进行处理。
  3. Flink优势:
    • 毫秒级延迟
    • 严格精确一次语义
    • 窗口API更灵活,语义更丰富
    • 提供事件时间语义,可以正确处理延迟数据。
    • 提供了状态。

三、Flink部署

web UI : 8081

Standalone模式、K8s模式略过。重点写YARN模式

部署模式分类

主要区别在于:集群的生命周期、资源的分配方式、应用的main方法到底在哪里执行(客户端还是JM)

  1. 会话模式(Session Mode)
    • 先启动一个集群,保持一个会话,在这个会话中通过客户端提交作业。
    • 因为资源共享,资源不够时新作业失败。
    • 同一个TM可能运行了很多作业,其中一个故障导致TM宕机 -> 影响其他作业
  2. 单作业模式(Per-Job Mode)
    • 每个提交的作业启动一个集群。(更好地隔离资源)
    • 客户端运行app后,启动集群;作业完成后,集群关闭。
    • 更加稳定,Per-Job模式需要借助资源管理框架来启动集群。
  3. 应用模式(Application Mode)
    • 前面两种方法应用代码都是在客户端执行,然后由客户端提交给JM。
    • 客户端需要占用大量网络带宽,将数据发送给JM。会加重客户端节点的资源消耗。
    • 不要客户端了,直接把app提交给JM。-> 为每个提交的任务单独启动一个JM(创建一个集群)
    • 这个JM只为执行这一个app而存在,执行结束后JM关闭。

YARN模式

客户端把Flink应用提交给YARN的ResourceManager,RM向NodeManager申请容器。在容器上,Flink会部署JobManager 和 TaskManager的实例,从而启动集群。

Flink会根据运行在JM上的作业所需要的Slot数量动态分配TM资源。

高可用

YARN的高可用,只启动一个JM,当这个JM挂掉之后,YARN会再次启动一个,利用YARN的重试次数来实现的高可用。

四、Flink运行时架构

image-20220813195507585

JM

任务管理和调度的核心

  1. JM接收客户端提交的 Jar包、数据流图(dataflow graph)、作业图(Job Graph)
  2. JM将作业图转换成物理层面的数据流图 -> 执行图(Execution Graph) 包含了所有可以并发执行的任务
  3. JM向RM申请资源,申请到后,将执行图发到TM上。
  4. RM的资源就是slot资源,包含一组执行计算的CPU和内存资源。
  5. 分发器Dispatcher,用来提交应用。

TM

工作进程

  1. 每一个TM都包含了一定数量的任务槽。slot是资源调度的最小单位,slot的数量限制了TM能够并行处理的任务数量。
  2. 启动TM后,TM会向RM注册slots,收到RM指令后,TM会将一个或者多个slot提供给JM调用。

宏观流程

image-20220813200444209

  1. 客户端通过分发器,将作业交给JM
  2. 分发器启动JM,将作业和作业图提交给JM
  3. JM将作业图解析为可执行的枝形图,得到资源数量,向RM请求slots
  4. RM判断当前资源是否足够,不够则启动新的TM
  5. TM启动后,向RM注册slots
  6. TM连接到JM,提供slots
  7. JM将所需任务分发给TM
  8. TM执行任务

YARN-Per-Job流程

image-20220813200933743

  1. 客户端将作业提交给YARN的RM,会将jar包和配置上传到HDFS
  2. RM启动容器资源,启动JM,并将作业提交给JM
  3. JM向RM(f)请求slots资源
  4. RM(f)向RM请求容器资源
  5. YARN启动新的TM容器
  6. TM启动之后,向RM(f)注册可用的slots
  7. RM(f)通知TM为新作业提供slots
  8. TM连接到JM,提供slots,JM将任务分发给TM执行任务。

重要概念

并行度

前一个操作处理完成,就发往下一步操作的节点。

任务有先后,这里关心的是数据的并行。

每一个算子可以包含一个或多个子任务,这些子任务在不同的线程、不同的物理机或不同的容器中完全独立地执行。

一个特定算子的子任务个数,被称为它的并行度。一个流程序的并行度,可以认为是其所有算子中最大的并行度。

并行度设置:代码中指定 > 提交时的命令行参数 > 配置文件

算子链

并行度相同的one to one 算子操作,可以直接链接在一起形成算子链。可以减少线程之间的切换和基于缓冲区的数据交换,减少时延的同时提升吞吐量。

四张图

逻辑流图 -> 作业图 -> 执行图 -> 物理图

任务和任务槽

  1. slots
    • 一个TM是一个JVM进程。可启动多个独立的线程,并行执行多个子任务。
    • 为了控制并发量,在TM上对每个任务所占用的资源做出明确划分 -> slot
  2. slots数量
    • 如果只要一个slot,那么每个任务运行在独立的JVM中。
    • 如果有多个JVM,则多个任务共享一个JVM。
    • slot只隔离内存,不隔离CPU
  3. 任务对slot的共享
    • 同一个作业,不同任务节点的并行子任务,可以放在同一个slot执行。

五、DataStream API

数据类型

Flink的类型基类 TypeInformation来同一表示数据类型。

由于Java泛型擦除的存在,在某些特殊情况(比如Lambda表达式),自动提取的信息推断不出类型。需要显示地提供类型信息。(.returns(Types.TUPLE(Type.STRING, Types.LONG)))

算子分类

  1. 转换算子
    • map, filter, flatMap
  2. 聚合算子
    • keyBy(聚合前要分区)
    • sum
    • min
    • max
    • minBy ,min只计算指定字段的最小值,minBy会返回包含字段最小值的整条数据
    • maxBy
    • reduce 一般化聚合
  3. UDF

image-20220813205910670

  1. 富函数类

    • 所有的Flink函数都有其Rich版本,一般以抽象类形式出现。
    • 复函数可以获取运行环境的上下文,并拥有一些生命周期方法,所以可以实现更复杂功能。
    • open()方法,初始化方法,文件IO创建、数据库连接、配置文件读取等一次性工作可以放在open中
    • close(),最后一个调用的方法啊,可以做清理用。
    • 富函数类提供了 getRuntimeContext()方法,可以获取运行时上下文信息,比如程序执行的并行度、任务名、状态.
  2. 物理分区

    • keyBy是逻辑分区,可能发生数据倾斜,或者需要手动分配分区策略时,可以使用物理分区。
    • 随机分配 .shuffle() 将数据按均匀分布随机传递到下游,每次执行结果不同。
    • 轮询分配 .rebalance()实现轮询重分区
    • 重缩放 .rescale() 也是轮询,只是当多个上游和多个下游时,轮询时1对all,冲缩放是1对n,多个上游共同分配all。
    • 广播 .broadcast() 数据会在不同分区都保留一份儿,可能进行重复处理。将输入数据复制并发送到下游算子的所有并行任务中去。
    • 全局分区 .global() 将所有的输入流数据都发送到下游算子的第一个并行子任务中去。强行让下游任务并行度变为1.
    • 自定义分区 .partitionCustom()

Sink算子

状态为啥不写入redis呢?因为写入redis一旦发生故障,需要复杂的机制保证恢复到之前的状态。

Flink内部提供了checkpoint来保证可以回滚到正确的状态。但是如果在处理过程中任意读写到外部系统,发生故障后就很难回退到从前了。

Flink与Kafka的连接器提供了端到端到精确一次语义。

六、Flink中的时间和窗口

在流式处理的过程中,数据是在不同的节点间不停流动的,由于网络延迟,上下游任务对于时间的理解也会有所不同。

时间语义

image-20220813212621320

水位线

用来度量事件时间,水位线之前的数据全部到达了。

划分了窗口后,根据数据的时间戳确定其属于哪个窗口。窗口处理的是有界数据,需要等窗口数据全部到齐才能计算出最终的统计结果。

本身窗口的时间进度可以通过时间戳标识,但是上下游之间的时间同步怎么办呢?比如下游有三个并行度,其中一个窗口接收到了9点的数据,窗口开始聚合运算,但是其他两个子任务并没有接收到这个数据,不能进行聚合运算。

水位线就是在数据流中加入一个时间标记,记录当前的事件时间,这个标记可以广播到下游,当下游任务收到这个标记,就可以更新自己的时钟。

image-20220814135013841

image-20220814135259380

image-20220814135518499

窗口

Flink中,窗口

image-20220814135737065

设置2s的延迟,窗口是左闭右开的,当12s的数据到来,[0,10)窗口关闭,12s数据进入到[10,12)中。

时间窗口、计数窗口:

是否按key分区

窗口函数 Window Function

image-20220814142214438

迟到数据处理

  1. 水位线,拨慢时钟

    image-20220814143059293

  2. 允许延迟,在不考虑水位线的情况下,使窗口晚一点再销毁。image-20220814142730207image-20220814143256768

  3. 侧输出流接收迟到数据。

image-20220814143319712

怎么将侧输出流的数据同步到之前的结果中呢?

七、处理函数

处理函数中,直面的就是数据流中最基本的元素:数据事件Event状态State时间time

富函数类:可以拿到状态、并行度、任务名等运行时信息

image-20220814143801103

top N

分流

可以利用处理函数分流,利用侧输出流分流。

八、多流转换

分流

  1. 侧输出流(推荐)
  2. filter 将一条流重放,不现实
  3. split 方法已经弃用

基本合流

  1. 联合 (Union)
    • 流中数据类型必须相同,暴力交汇
    • 不同流中水位线进度不同,合流之后的水位线以最小的为准。
  2. 连接 (Connect)
    • 允许类型不同,得到的是连接流(Connected Streams)。形式“统一”,彼此之间独立。
    • 变回Data Stream 定义co-process 说明对于不同的流分别怎么处理。
    • 可调用的co-process方法有 map、flatMap、process,传入一个Co***Function实现类
  3. 广播连接流 (BroadcastConnectedStream)

image-20220814151852131

基于时间的合流--双流join

两条流的合并,前面都是将流放在一起,我们有时更希望根据某个字段的值将流联结起来,配对去做处理。

也可以利用Connect和key by实现,不过有些抽象。

窗口联结 Window Join

image-20220814153028711

image-20220814153542166

image-20220814153551779

image-20220814153633373

间隔联结 Interval Join

image-20220814153742750

image-20220814153753632

image-20220814153827028

image-20220814153846245

image-20220814153948236

窗口同组联结 Window CoGroup

image-20220814155736905

双流join优化与总结

  1. 双流join时间到了不触发、没输出
    • wm是否合理,数据事件是否远远大于wm和窗口时间
  2. 双流join利用的是state数据,state保存多久,会内存爆炸吗?
    • state自带有ttl机制,可以设置ttl过期策略,建议程序中的state用完之后手动clear
  3. 双流join数据倾斜
    • 过滤异常key
    • 拆分表减少数据
    • 打散key分布
  4. 多流join
    • 先union再后续处理
    • 先connect再join
  5. join过程延迟,没有关联上的数据会丢失吗?
    • 侧输出流可以存储延迟流
    • 节点网络异常,检查点可以保证数据不丢失。

九、状态编程

状态由任务维护,用来计算输出结果的所有数据。

有状态算子:聚合算子、窗口算子

无状态算子:基本转换算子

状态

image-20220814160527178

image-20220814160618590

image-20220814160803004

按键分区状态 Keyed State

可以通过复函数获得状态

状态生存时间 TTL

image-20220814161252594

image-20220814161327882

算子状态

应用场景较key state少,主要应用在Source 和 Sink到外部系统的算子上,或者完全没有key的场景。

Flink的Kafka连接器使用了算子状态。Kafka消费者的每一个并行实例,都会为对应的主题(topic)分区维护一个偏移量,作为算子状态保存起来。

image-20220814162225136

image-20220814162405395

广播状态

image-20220814162525647

状态后端

image-20220814162714333

image-20220814162827156

状态后端分类:

image-20220814163104411

十、容错机制

checkpoint

在有状态的流处理中,任务继续处理新数据,并不需要“之前的计算结果”,而是需要任务“之前的状态”。实现容错 -> 将某个时间点的状态保存下来,这份存档即是 checkpoint

周期性存档

image-20220814163959783

image-20220814164026136

状态的恢复:精确一次的状态一致性保证,故障发生前的结果并没有保存到新状态中,不会对结果产生影响。

image-20220814164835892

检查点算法:分布式快照

image-20220814165121973

image-20220814165201857

image-20220814165856081

image-20220814170031433

状态一致性

端到端精确一次

Flink和Kafka连接时的精确一次保证

需要的配置

image-20220814185653907

十一、Table API和SQL

大致流程:

  • 流执行环境
  • 流读取数据
  • 表执行环境
  • 流转表
  • 处理逻辑
  • 表转流

image-20220814201304571

image-20220814201317632

流处理中的表

image-20220814201600264

复杂时间处理 Complex Event Processing

参考

《剑指大数据》

标签:总结,状态,窗口,Flink,JM,TM,算子
来源: https://www.cnblogs.com/ogleede/p/16586230.html