Flink

  • Flink是一个有状态的流式计算,意味着可以存放中间计算结果
一.Flink部署

放表格

 

二.一些重要概念

  • 并行度:一个算子的子任务就是并行度,比如一个map有2个subTask,并行度就是2,每一个subTask占用一个slot槽。整个流程序的并行度是算子中最大的并行度
  • 算子链:为了资源的充分利用,前后相连接的算子可以合并在一起,形成一个算子链,放在一个task中执行。所以我们看到好多dataflow图,一个框里有多个算子。
    • 前提条件:算子之前有2种关系:一对一,比如source到map;重分区:一对多的关系,比如keyBy和window,并行度为2,会根据key的值,有数据流会发送改变。
    • 形成条件:并行度一样&&一对一关系的算子会形成一个算子链、
  • 图:
    • StreamGraph(逻辑流图):主要是根据DataStream在客户端生成的图
    • JobGraph(作业图):对逻辑流图进行优化,主要是合并算子链
    • ExecutionGraph(执行图):jobManager执行,主要是作业图并行化版本,按照并行度进行拆分
    • Physical Graph(物理图):TaskManager执行,主要确认数据存放位置和收发方式,然后就可以对过来的数据进行处理计算
  • 子任务,任务槽,并行度
    • 子任务(subTask):就是一个并行度任务,每个子任务都需要一个线程
    • 任务槽(task Slots):每一个taskManager就是一个jvm进程,那么将进程划分成多个线程,每一个线程就是一个slots槽
      • 共享任务槽:如果slots槽不够,多个subTask可以公用一个slots槽。主要是为了将资源密集型和非密集型的任务同时放到一个slot中,它们就可以自行分配对资源占用的比例,从而保证最重的活平均分配给所有的slots,而不是有的slot任务轻,有的重。如图:
    • 并行度:我们程序设置的并行度受限于solt的数量,比如solt为5,并行度为6,实际并行度还是5

 三.DataStream

获取运行环境:

// 获取执行环境
StreamExecutionEnvironment.getExecutionEnvironment();
// 设置执行模式
execution.runtime-mode=STREAMING(流处理,默认)/BATCH(批处理)/AUTOMATIC(自动选择)

源/输出算子:

env.addSource();
env.addSink();
// 自定义Source
1.实现SourceFunction接口,重新run()
2.新架构的Source:吃透 Flink 架构:一个新版 Connector 的实现 - 知乎 (zhihu.com)

转换算子:

基本转化算子:

  • map:一一映射,将数据进行转换,形成新的数据
  • flatMap:扁平化映射,一对多,可以过滤。结合了map和filter的特性。比如将wordCount程序的输入数据,进行转化: 
  • filter:数据过滤

 聚合算子:Flink做聚合计算,就是把计算中间结果保存起来。要做聚合,需要先进行分区(keyBy),分区后执行sum等聚合运算,flink就会存储中间计算结果,一直累加

  • keyBy:按照某个key进行分区,分区后中间计算结果会被保存
  • 简单聚合:使用sum(),max(),min()等内置方法
  • 规约聚合:使用reduce()方法自定义统计方法。主要可以实现ReduceFunction接口进行自定义

自定义函数:如上的算子map,filter,reduce等,需要写具体逻辑,就要实现RichMapFunction、RichFilterFunction、 RichReduceFunction等接口,这些都是叫自定义函数

物理分区:keyBy是逻辑分区,实际真正每个key分到哪个slot是不知道的,就可能造成某一个分区key的数量很多,就会造成某一个slot的压力很大。所以flink提供一些方法供我们做底层逻辑分区

  • 随机分区:shuffle()
  • 轮询分区:rebalance()
  • 重缩放分区:rescale(),就是分组进行轮询分区,只在自己的组进行轮询
  • 广播分区:broadcast(),让每一个slot都重复的接收算子
  • 全局分区:global(),很极端,让所有的算子都分配到一个slot上
  • 自定义分区:Custom(),按照自己的要求进行分区

四.时间和窗口

  • 时间:时间是存在差异的,一条数据有事件时间(产生时间),也有处理时间(就是在flink处理的时间)。比如处理8-9点数据,是处理8-9点产生的数据呢?还是处理8-9到达flink的数据呢?
    • 显然一般都是处理事件时间,就是按照数据产生的时间。
    • 有问题,事件时间其实就是一个数据的一个标识,比如像日志数据那样,那么窗口在处理数据要看这个标识。如果数据一直没有到来,下游算子是没办法知道时间的,所以就需要定义一个公共的"逻辑时间"
  • 水位线:在 Flink 中,这种用来衡量事件时间进展的标记,就被称作“水位线”(Watermark)
    • 分类:
      • 每个数据更新一下水位线
      • 周期性更新水位线:因为数据量太大,时间戳可能重复,水位线会被重复更新,所以一般采用周期性更新
    • 乱序问题:理想情况下,数据是按序来的,但是不排除有乱序的情况,水位线怎么处理
      • 水位线一定是递增的,只有发现新来的数据比水位线时间戳大,才更新水位线,小的话不更新
      • 对于晚来的数据,可以根据经验设置水位线延迟,比如一般乱序3s左右,设置水位线慢3s,窗口就可以慢3s。其他没等到的数据就会丢失

    • Watermark = 当前最大事件时间 - 乱序时间 - 1ms
    • 水位线的传递:是依靠数据进行传递,但是数据如果分区多个子任务,有的水位线快有的水位线慢,下游数据要根据"之前数据都到齐"原则,取最慢的水位线

  •  窗口:流处理是无界的,但是我们一般会统计一个"范围"之内的数据,这时候就需要将无界流变为无数个有界流(数据块)进行处理,这些数据块就是窗口。
    • 和聚合函数不同的是,聚合函数会来一条数据,更新输出一条结果,而窗口是一批数据输出一个结果
    • 窗口是动态创建的,同时可以有多个窗口存在,如图:
    • 分类:
      • 按照驱动类型:时间窗口(按照水位线);计数窗口(人满发车)
      • 按照分配规则:
        • 滚动窗口:类似窗口不停向后翻滚,首尾相连,没有重复部分。比如计算8-9的数据
        • 滑动窗口: 类似窗口不停向后滑动,每次滑动一步,但是窗口大小不变,有重复部分。比如统计24小时内数据
        • 会话窗口:类似打电话,有数据到就开启会话(开启窗口),没有数据就关闭会话(关闭窗口)。比如做一些报警处理,有问题才弄个窗口计算指标数据
        • 全局窗口:全局窗口没有关闭窗口一说,需要你自定义触发器,进行关闭窗口和开启窗口

    • 组成部分:
      • 窗口分配器:就是决定使用什么类型窗口
      • 窗口函数: 就是窗口做什么计算

五.合流和分流

分流:

  • process Function 的测出流(side output)

合流:

  • Union:可以合并多条流,数据类型是一致的。
  • Connect:只能合并2条流,数据类型可以不一直,主要是合成一个流后做map操作区分数据类型。
  • join:基于时间的合流,参考资料     Flink详述双流 Join 3 种解决方案 + 2 种优化方案_flink双流join数据延迟-CSDN博客
    • window join:先进行join,再进行窗口统计  
    • window coGroup:相当于window join只能内连接,coGroup可以内连接外连接
    • internel join:间隔join,根据一个元素,取它上限和下限的范围,在另一个流中找这个范围内匹配的数据。::

六.状态和容错机制

状态:

  • 在流处理中,数据是连续不断到来和处理的。每个任务进行计算处理时,可以基于当前数据直接转换得到输出结果;也可以依赖一些其他数据。这些由一个任务维护,并且用来计算输出结果的所有数据,就叫作这个任务的状态。状态有各种各样结构的数据类型,有的算子有状态有的算子没有状态。有状态:source,sink,sum等  无状态:map。
  • 状态持久化(使用检查点):所有任务的状态在某个时间点的一个快照,一般将这个快照保存在分布式文件系统,如果出现故障进行恢复
  • 状态后端:因为状态是分布式的,所以状态的存储,访问,维护都需要一个可拔插的组件决定,这个组件就叫做"状态后端"
    • 主要做2件事:1.本地的状态管理(tm的内存/rocksDB); 2.将检查点写入到远程的持久化存储(hdfs)

容错机制:

  • checkpoint:Flink周期性保存。在所有算子(任务)都恰好处理完一个相同的数据的时候,将它们的状态保存下来。(每个算子保存的的状态要一致,不能有的快有的慢)
  • 恢复:当发生故障重启时,一般时找到最近一次成功保存的检查点来恢复状态
  • 检查点算法:在保存状态的时候,还是会有新的数据进来,难道让其他算子都暂停,然后保存数据吗?为了解决这个问题,引入了检查点分界线(Barrier)
    • Barrier:就相当于一个标识,其实就是jobmanager向taskmanager定时发送一个barrier(分界线),barrier中携带检查点id,随着数据向下游算子流去,source->map->sink。当barrier到达最后一个算子sink时,前面经过的算子都保存了状态,所有状态一致,开始保存状态。
    • 分布式Barrier:如果多个任务都在进行Barrier,有的快有的慢,怎么处理?那就在下游设置分界线对齐操作,需要等到所有并行分区 的 barrier 都到齐,才可以开始状态的保存。
  • savepoint:就是我们手动触发一个状态的保存,再次启动时,指定这个状态。
    • 触发保存点的方法:
      • 单独保存:bin/flink savepoint :jobId [:targetDirectory]
      • 停止时保存(推荐):bin/flink stop --savepointPath [:targetDirectory] :jobId
    • 从保存点重启:bin/flink run -s :savepointPath [:runArgs]

状态一致性:

  • 最多一次(at-most-once):发生故障,直接重启。之前的运行的状态不管
  • 至少一次(at-least-once):保证之前状态不丢,再次重启延续之前状态,但是有可能有重复数据、
  • 精确一次(exactly-once):数据不丢不重复

要想实现精确一次,就要求输入源支持数据重放,Flink支持数据状态保存恢复,输出源支持事务/幂等写入

经典例:Kafka作为输入端和输出端,Flink作为中间处理。如何保证精确一次:

  • 输入端Kafka:支持状态重放,Flink Kafka connect保存Kafak消费点位,故障可以重新读取数据
  • Flink:支持状态保存和恢复
  • 输出端Kafka:保证事务(支持二阶段提交),Flink开启状态保存出现一个barrier,barrier到达sink算子开启一个事务。接下来所有barrier后的数据都会进入预提交阶段,当jobManager通知保存checkpoint的时候,才进入正式提交阶段。这样其实保证了kafka和flink同存一致的状态,恢复的时候肯定就不会有重复数据。

七.Flink SQL

CataLog:

  • 提供元数据信息,例如库表信息等。是数据库之上的一层概念,相当于一个连接(目录)
  • 类型:
    • 基于内存:每一个任务运行都会建立一个defaule_catalog,这个是基于内存的,任务运行完就消亡
    • jdbcCataLog:支持连接关系型数据库:MySQL和Postgres。不支持自己create表,只支持MySQL现有的表
    • HiveCatalog:连接hive的metastore。俩个作用:1.作为Flink元数据持久化存储(只要是在这个catalog创建任何类型的表,都可以存储到这里)  2.作为读写hive数据的接口。
  • 注意:
    • create catalog这个语句是每次运行作业都需要定义,而真正create table,在hiveCatalog中是持久化保存,再次运行也会存在。
    • 使用catalog中的表是三段式:catalog名.库名.表名
    • 使用jdbc或者hiveCataLog只需要将jdbc-connect或者hive-connect的jar包放入flink 的lib目录下

自定义函数:一般继承xxFunction,重写eval()

  • 标量函数(Scalar Functions):一对一,
  • 表函数(Table Functions) :一对多

  • 聚合函数(Aggregate Functions):多对一
  • 表聚合函数(Table Aggregate Functions):多对多

八.提交流程

  • 客户端底层就是YarnClusterDescriptor,部署perjob模式在客户端执行main,session和application不在客户端执行main
    • perjob执行main主要是:执行用户main代码,执行execute()生成jobGroup
  • ClusterEntryPoint:就是执行AM的入口类,application模式中会读取pipeline.jar,读取用户的代码
  • 轻量化的改造是合并Flink1.16的sql-gateway,在启动session的时候启动sql_gateway。这样可以通过rest接口提交作业
    • 对于Flink sql 的提交:其实就是sql_gateway通过系列转换,将sql转换成算子,TableEnvironmentImpl.executeSql()最终会生成Transformation(转换算子)->StreamGraph->jobGraph->xxx
      • 对于DDL语句(create,show等)都是创建一个catelog,相当于存了一份表。
      • 对于DML语句,都是一个作业,真正把输入源、输出源连接在一起执行的(比如insert和select语句)

k8s提交模式:

  • 第一个阶段:启动 Session Cluster。Flink Client 内置了 K8s Client,告诉 K8s Master 创建 Flink Master Deployment,ConfigMap,SVC。这些k8s对象创建完成后,Master 就拉起来了。这时 Session 已经部署完成,因为是k8s navtive 模式并没有创建维护任何 TaskManager。
  • 第二个阶段:当用户提交 Job 时,可以通过 Flink Client 或者 Dashboard 的方式,然后通过 Service 到 Dispatcher,Dispatcher 会产生一个 JobMaster。JobMaster 会向 K8sResourceManager 申请资源。ResourceManager 会发现现在没有任何可用的资源,它就会继续向 K8s 的 Master 去请求资源,请求资源之后将其发送回去,起新的 Taskmanager。Taskmanager 起来之后,再注册回来,此时的 ResourceManager 再向它去申请 slot 提供给 JobMaster,最后由 JobMaster 将相应的 Task 部署到 TaskManager 上。这样整个从 Session 的拉起到用户提交都完成了。

 和yarn的主要区别:

  • 存储:hdfs和pvc
  • 调度:yarn的rm和k8s的master
  • 配置文件:本地和ConfigMap
  • 部署k8s资源主要通过:pod_template
九.Flink sql gateway

  • SqlGatewayEndpoint:基于RestServerEndpoint实现的Netty服务,对外提供Rest Api
  • SessionManager :会话管理器,管理session创建与删除;
  • Session:一个会话,里面存放着任务所需要的Flink配置和上下文环境信息,负责任务的执行;
  • Classpath:Flink Sql Gateway启动时会加载flink安装目录的classpath,所以flink sql gateway 基本上没有除flink以外的相关依赖。

flink sql转变为算子:

TableEnvironmentImpl.executeSql()最终会生成Transformation(转换算子)->StreamGraph->jobGraph

  • 对于DDL语句(create,show等)都是创建一个catelog,相当于存了一份表。
  • 对于DML语句,都是一个作业,真正把输入源、输出源连接在一起执行的(比如insert和select语句)

 十.内存模型

  • JVM管理内存,在大数据场景下有很多问题,Flink就用上面模型自主管理内存。
    • 比如对象存储密度低(本来boolean需要1byte,为了对其用了8byte);经常GC时间长;OOM影响整个程序等 
  • Flink将内存划分多个内存段(MemorySegment),每个内存段大约32KB。自动提供对二进制的读写,不用序列化

十一.反压

  • 原因:每一个任务(算子),都有一个输入缓存区(RP),输出缓存区(IG)。当缓存区的数据来不及消费时,就会堆积,造成上游缓存区也堆积,最后没有新数据流入
  • Credit-based反压机制:就是每次taskManager之间传递数据,每传递一个,都有个响应,告诉当前缓存区还能存多少数据,当缓存区数据满了,就不会再发送新数据
  • 分析手段:Flink web ui
  • 反压造成后果:
    • checkpoint保存失败,比如超过保存时间
    • 影响state变大,barrier到达快的,会放入缓存区,等待到达慢的barrier,就会把缓存区所有数据都存,导致state变大
  • 解决方案:
    • 是否数据倾斜:就是有某个算子,计算压力太大,数据都阻塞到那里。
      • 数据量大,没有设置并发度,比如读取kafka的source算子
      • key分布不均匀,无统计,这样可以在key前缀加上随机数,确保数据不会分布到某一个任务中
      • key分布不均匀,需要统计,预聚合+聚合。
    • 资源是否满足
    • GC是否影响
    • 外部数据源读取是不太慢
posted @ 2024-03-28 16:36  小猴子_X  阅读(70)  评论(0)    收藏  举报