Flink知识概要

Flink

一、基本特性

1、Flink简介

Flink 是分布式实时和离线计算引擎,用于在无界数据流和有界数据流上进行有状态的计算, 能在常见集群环境中运行,并能以内存速度和任意规模进行计算。

应用场景包括:实时数据计算、实时数据仓库和 ETL、事件驱动型场景,如告警、监控;此外,随着 Flink 对机器学习的支持越来越完善,还可以被用作机器学习和人工智能

2、Flink特性

  • 1.批流一体:Flink从另一个视角看待流处理和批处理,将二者统一起来,流处理看待时输入数据流是无界的;批处理被作为一种特殊的流处理,只是它的输入数据流被定义为有界的。
  • 2.Exactly-Once:Flink 通过实现两阶段提交状态保存来实现端到端的精确一致性语义
  • 3.状态管理:Flink在做计算的过程中经常需要存储中间状态,来避免数据丢失和状态恢复。
  • 4.时间处理:Flink支持事件时间EventTime、注入时间IngestionTime、处理时间ProcessingTime三种时间,同时也支持watermark来处理滞后数据。
  • 5.支持窗口:支持时间驱动的 timeWindow、数据驱动的countWindow,同时支持滚动窗口tumbling windows、滑动窗口sliding windows、会话窗口session windows。滚动窗口中的数据不会叠加;
  • 6.利用内存性能:任务的状态始终保留在内存中,如果状态大小超过可用内存,则会保存在能高效访问的磁盘数据结构中,非常低的处理延迟,

性能上: ProcessingTime性能最好, IngestTime次之, EventTime最差
延迟上:EventTime延迟最低,IngestTime次之, ProcessingTime延迟最高
确定性: EventTime确定性最高,IngestTime次之, ProcessingTime最低

3、精确一次Exactly-Once

https://developer.51cto.com/article/643945.html

精确一次消费的概念,出现在消息系统中,通常关注点只和消息的消费有关,但是实际情况远不止如此

端到端的精确一次消费包括了,从消息源发出消息,处理程序接到消息并处理消息,到最后保存结果,整个流程,数据的精确一次处理。

在 Flink 中,端到端精准一次处理的位置有三个:

  • 1)Source 端:数据从消息系统进入到 Flink 时,需要保证消息精准一次消费,发生故障时需要支持重设数据的读取位置,如Kafka可以通过offset来实现 ,可以自主维护消息的offset,实现精确一次消费
  • 2)Flink 内部端:flink利用 Checkpoint 机制,把状态存盘,发生故障的时候可以恢复,保证内部的状态一致性。
  • 3)Sink 端:将处理完的数据sink到下一阶段时,需要保证数据能够准确无误发送到下一阶段。Flink 1.4 版本引入了两阶段提交 Sink,
    在这里插入图片描述
    在这里插入图片描述

两阶段提交:

  • 1)开始事务:创建一个临时文件夹,来写把数据写入到这个文件夹里面;
  • 2)预提交:将内存中缓存的数据写入临时文件并关闭;
  • 3)正式提交:将之前写完的临时文件放入目标目录下。这代表着最终的数据会有一些延迟;
  • 4)丢弃:丢弃临时文件;

若失败发生在预提交成功后,正式提交前。可以根据状态来提交预提交的数据,也可删

Flink+Kafka 如何实现端到端的 exactly-once 语义

  • Flink内部 —— 利用 checkpoint 机制,把状态存盘,发生故障的时候可以恢复,保证内部的状态一致性
  • source —— kafka consumer 作为 source,可以将偏移量保存下来,如果后续任务出现了故障,恢复的时候可以由连接器重置偏移量,重新消费数据,保证一致性
  • sink —— kafka producer 作为 sink,采用两阶段提交 sink,需要实现一个TwoPhaseCommitSinkFunction
    在这里插入图片描述

4、状态State

状态State是指,流计算过程中计算节点的中间计算结果或元数据属性,比如 在aggregation过程中要在state中记录中间聚合结果。流计算在增量计算Failover失败重启都需要state的支撑。

Flink状态主要分为两种类型:

  • 1)键控状态Keyed State:状态跟特定的key绑定,API中的keyBy字段,每一个key都有一个属于自己的State,key与key之间的State是不可见的,KeyedState 只能使用在KeyStream上的操作和函数。支持的数据结构有5种ValueState、ListState、MapState、AggregatingState、ReducingState

  • 在这里插入图片描述

  • 2)算子状态Operator State:状态跟一个特定算子的实例绑定,整个算子只对应一个State对象(相同的并行算子都能访问到状态)。只支持ListState

  • 在这里插入图片描述

可以将Keyed State视为是已经被分片或分区的Operator State,每个key都有且仅有一个状态分区(state-partition)。

状态后端State backend
主要负责本地的状态管理,以及将检查点(checkpoint)状态写入远程存储

  • 1)MemoryStateBackend: 状态信息是存储在 TaskManager 堆内存中的,checkpoint 的时候将状态保存到 JobManager 的堆内存中。
    缺点:只能保存数据量小的状态,状态数据有可能会丢失;优点:开发测试很方便
  • 2)FsStateBackend:状态信息存储在 TaskManager 堆内存中的,checkpoint 的时候将状态保存到指定的文件中 (HDFS 等文件系统)。
    缺点: 状态大小受TaskManager内存限制(默认支持5M) ;优点: 状态访问速度很快、 状态信息不会丢失;用于: 生产,也可存储状态数据量大的情况
  • 3)RocksDBStateBackend:状态信息存储在 RocksDB 数据库 (key-value 的数据存储服务), 最终保存在本地文件中 checkpoint 的时候将状态保存到指定的文件中 (HDFS 等文件系统)
    缺点: 状态访问速度有所下降 优点: 可以存储超大量的状态信息 状态信息不会丢失 用于: 生产,可以存储超大量的状态信息

5、水位线WaterMark

WaterMark水位线,可以理解为 一个延迟触发机制,,Flink窗口计算时,通过结合EventTime与WaterMark,来处理迟到的事件,一般等于currentMaxEventTime - delay,是一个具体时间戳,认为事件 eventTime小于WaterMark的所有数据都已经到达,

如果有窗口的停止时间等于Watermark,也就是Watermark大于某个window的end_time,并这个window左闭右开有数据,那么这个窗口被触发执行。

只有一个waterMark时,一个线程触发当前窗口的waterMark时间,为最大的waterMark时间;
当有多个waterMark时,也就是多并行度多个线程时,触发当前窗口的waterMark时间,取线程间最小的线程内最大waterMark时间

每个事件都有一个CurrentWatermark,相当于每个事件的计算触发时间,由ProcessTime 或者EventTime 改变为 当前watermark时间

Watermark计算方式一般为;

 currentMaxEventTime = Math.max(currentMaxEventTime, currentElementEventTime);
new Watermark(currentMaxEventTime - maxOutOfOrderness);

正常事件:EventTime大于currentMaxEventTime,窗口的currentMaxEventTime等于EventTime,不受影响;晚到事件,EventTime小于currentMaxEventTime,窗口的currentMaxEventTime等于上一个大的EventTime,

watermark窗口触发条件:

1.watermark 时间 >= window_end_time
2.[window_start_time, window_end_time) 区间中有数据存在,注意是左闭右开的区间,而且是以 event time 来计算的

主要是作用于有窗口重叠的,Flink的窗口不同于spark的窗口,每隔5秒统计一次,不管窗口里面有没有数,到窗口结尾就触发统计,flink的窗口需要一个大于窗口时间的事件来触发,增加waterMark,增加容错,窗口统计会晚设置的延迟几秒

6、容错checkpoint

容错:

  • MySQL的思想很容易理解,就像棋谱一样,把每一步都记录下来,binlog。后人读棋谱,可以随时切换到任意一张棋谱,然后跟着每一步的操作重现当时的情景。
  • HDFS的思想也比较好理解,怕丢数据,就存成N份。只要写进去最少副本数,就自动会把所有旧副本都覆盖了,最大程度的保存好数据。而且他们都属于离线数据库,随时可以存一个快照。
  • 但Flink不一样,MySQL和HDFS都是离线存储,Flink是在线的,是一个数据流呀,不能停,Flink通过Checkpoint机制将某个时刻应用状态State进行快照Snapshot保存到磁盘。

Flink 实现容错,主要通过 状态Checkpoint机制 。State 用来存储计算过程中的中间状态。Checkpoint 将某个时刻应用状态State进行快照Snapshot保存到磁盘

Flink中的Checkpoint,底层使用了Chandy-Lamport分布式快照算法,可以保证数据的在分布式环境下的一致性。主要核心是barrier栅栏,相当于标识符,表示对应用中各个OperatorState进行快照时,到哪个位置。

checkpoint流程

  • 在checkpoint过程中,JobManager会按照代码配置的规则,定期触发checkpoint,在所有的source的子任务中注入Barrier栅栏

  • TaskManager在收到所有上游广播的Barrier栅栏(理解为执行Checkpoint的信号) 后,这 个barri栅栏随着数据流向一直流动,当流入到一个算子的时候,算子会等待所有流的 Barrier 都到达之后,才会开始本地的快照,这种机制被称为 Barrier 对齐。 flink 1.11后可以非Barrier 对齐,触发checkpoint。

  • 在对齐的过程中,算子只会继续处理的来自未出现 Barrier Channel 的数据,而其余 Channel 的数据会被写入输入队列,直至在队列满后被阻塞。当所有 Barrier 到达后,算子进行本地快照,输出 Barrier 到下游并恢复正常处理。

  • 当整个DAG图的子任务的checkpoint都做完之后,会汇报给JobManager,JobManager则认为这个checkpoint已经完成。

  • 流的 barrier ,是 Flink 的 Checkpoint 中的一个核心概念。 多个 barrier 被插入到数据流中,然后作为数据流的一部分随着数据流动(有点类似于 Watermark),这些 barrier 不会跨越流中的数据。
  • 每个barrier 会把数据流分成两部分:一部分数据进入当前的快照 ,另一部分数据进入下一个快照。
  • 每 个barrier 携带着快照的 id,barrier 不会暂停数据的流动, 所以非常轻量级。
  • 在流中, 同一时间可以有来源于多个不同快照的多个 barrier, 这个意味着可 以并发的出现不同的快照.

Flink的checkpoint机制,可以与(stream和state)的持久化存储交互的前提: 持久化的source,它需要支持在一定时间内重放事件。这种sources的典型例子是持久化的消息队列(如 Kafka,RabbitMQ等)或文件系统(比如HDFS等
https://developer.aliyun.com/article/926456
在这里插入图片描述

https://blog.csdn.net/qq_24095055/article/details/124564178
Flink Checkpoint 基于 Chandy-Lamport 算法的分布式快照

Chandy-Lamport 算法,将分布式系统抽象成 DAG,节点表示进程,边表示两个进程间通信的管道。分布式快照的目的,是记录下整个系统的状态,即可分为节点的状态(进程的状态),和边的状态(传输中的数据)。因为系统状态是由输入的消息序列,驱动变化的,我们可以将输入的消息序列分为多个较短的子序列,图的每个节点或边先后处理完某个子序列后,都会进入同一个稳定的全局统状态。利用这个特性,系统的进程和信道在子序列的边界点分别进行本地快照,即使各部分的快照时间点不同最终也可以组合成一个有意义的全局快照

Flink 通过在 DAG 数据源,定时向数据流注入名为 Barrier 的特殊元素,将连续的数据流切分为多个有限序列,对应多个 Checkpoint 周期。每当接收到 Barrier,算子进行本地的 Checkpoint 快照,并在完成后异步上传本地快照,同时将 Barrier 以广播方式发送至下游。当某个 Checkpoint 的所有 Barrier 到达 DAG 末端且所有算子完成快照,则标志着全局快照的成功。

比起其他分布式快照,该算法的优势在于辅以 Copy-On-Write 技术的情况下不需要 “Stop The World”影响应用吞吐量,同时基本不用持久化处理中的数据,只用保存进程的状态信息,大大减小了快照的大小。

当作业出现反压时,阻塞式的 Barrier 对齐反而会加剧作业的反压,甚至导致作业的不稳定。

  • 首先, Chandy-Lamport 分布式快照的结束依赖于 Marker 的流动,而反压则会限制 Marker 的流动,导致快照的完成时间变长甚至超时。
  • 其次,Barrier 对齐本身可能成为一个反压的源头,影响上游算子的效率,而这在某些情况下是不必要的。

flink是基于Chandy-Lamport算法来实现全局快照的,其核心就是在数据中间穿插barrier;当一个task上游同一批次所有的barrier到齐时,就可以触发快照状态的保存了,问题就是出在这里,等待对齐。在flink1.11版本引入了非对齐的checkpoint,来解决这种阻塞问题,

  • 如果当第一个barrier来的时候,我不能触发checkpoint, 是因为还有部分数据没有处理到。

  • 直接把这部分还没处理的数据(在buffer里面的数据),连同状态数据一起保存到checkpoint里面;

  • 在从checkpoint恢复的时候,就先把这部分buffer数据, 先恢复到当前task的buffer里面,继续计算就可以了,其实弱化了每个checkpoint批次的概念;

  • 这样一来当收到第一个barrier的时候,就可以直接触发checkpoint了

对齐与非对齐,两者的差异主要可以总结为两点:

  • 快照的触发,是在接收到第一个 Barrier 时还是在接收到最后一个 Barrier 时。
  • 是否需要阻塞已经接收到 Barrier 的 Channel 的计算。

7、Flink内存管理

Flink本身基本是以Java语言完成的,理论上说,直接使用JVM的虚拟机的内存管理就应该更简单方便,但Flink还是单独抽象出了自己的内存管理。

https://blog.51cto.com/u_15294985/5134340

因为Flink是为大数据而产生的,而大数据使用会消耗大量的内存,而JVM的内存管理管理设计是兼顾平衡的,不可能单独为了大数据而修改,这对于Flink来说,非常的不灵活,而且频繁GC会导致长时间的机器暂停应用,这对于大数据的应用场景来说也是无法忍受的。

JVM在大数据环境下存在的问题:

  • 1)Java 对象存储密度低。在HotSpot JVM中,每个对象占用的内存空间必须是8的倍数,那么一个只包含 boolean 属性的对象就要占用了16个字节内存:对象头占了8个,boolean 属性占了1个,对齐填充占了7个。而实际上我们只想让它占用1个bit。
  • 2)在处理大量数据会生成大量对象,尤其是几十甚至上百G的内存应用时
  • 3)Java GC可能会被反复触发,其中Full GC或Major GC的开销是非常大的,GC 会达到秒级甚至分钟级。
  • 4)OOM 问题影响稳定性。OutOfMemoryError是分布式计算框架经常会遇到的问题,当JVM中所有对象大小超过分配给JVM的内存大小时,就会发生OutOfMemoryError错误,导致JVM崩溃,分布式框架的健壮性和性能都会受到影响。

在这里插入图片描述
Flink的内存管理,是在JVM的基础之上封装的内存管理;除了JVM之上封装的内存管理,还会有个一个很大的堆外内存(可以理解为使用操作系统内存),用来执行一些IO操作。

taskmanager进程内存 = Flink内存 +JVM特有内存
Flink内存 = 框架堆内和堆外内存 + Task堆内和堆外内存 + 网络缓冲内存+管理内存

  • 1) JVM 特定内存:JVM 本身使用的内存,包含 JVM 的元空间和 over-head
    • JVM 的元空间,默认 256mb
    • JVM over-head 执行开销:JVM 执行时自身所需要的内容,包括线程堆栈、IO、编译缓存等所使用的内存。.fraction默认 0.1;min,默认 192mb;max,默认 1gb
  • 2) 框架内存:Flink 框架,即 TaskManager 本身所占用的内存,不计入 Slot 的资源中
    • 堆内内存:size默认 128MB
    • 堆外内存:size默认 128MB
  • 3) Task 内存:Task 执行用户代码时所使用的内存
    • 堆内内存:size默认 none,由 Flink 内存扣除掉其他部分的内存得到。
    • 堆外内存:size默认 0,表示不使用堆外内存
  • 4) 网络内存:网络数据交换所使用的堆外内存大小,如网络数据交换缓冲区
    • 堆外内存: fraction,默认 0.1 ; min,默认 64mb; .max,默认 1gb
  • 5) 托管内存:用于 RocksDB State Backend 的本地内存和批的排序、哈希表、缓存中间结果。
    • 堆外内存: fraction,默认 0.4; size,默认 none如果 size 没指定,则等于 Flink 内存*fraction

Flink相对于Spark,堆外内存该用还是用, 堆内内存管理做了自己的封装,不受JVM的GC影响 。

基于Yarn模式,一般参数指定的是总进程内存,taskmanager.memory.process.size,
比如指定为 4G,每一块内存得到大小如下:
(1)计算 Flink 内存
JVM 元空间 256m
JVM 执行开销: 4g*0.1=409.6m,在[192m,1g]之间,最终结果 409.6m

Flink 内存=4g-256m-409.6m=3430.4m
(2)网络内存=3430.4m0.1=343.04m,在[64m,1g]之间,最终结果 343.04m
(3)托管内存=3430.4m
0.4=1372.16m
(4)框架内存,堆内和堆外都是 128m
(5)Task 堆内内存=3430.4m-128m-128m-343.04m-1372.16m=1459.2m

8、Flink的 序列化与反序列化

Flink除了对堆内内存做了封装之外,还实现了自己的序列化和反序列化机制,

序列化与反序列化可以理解为编码与解码的过程。序列化以后的数据希望占用比较小的空间,而且数据能够被正确地反序列化出来。为了能正确反序列化,序列化时仅存储二进制数据本身肯定不够,需要增加一些辅助的描述信息。此处可以采用不同的策略,因而产生了很多不同的序列化方法。

目前,绝大多数的大数据计算框架,都是基于JVM实现的,为了快速地计算数据,需要将数据加载到内存中进行处理。

当大量数据需要加载到内存中时,如果使用Java序列化方式来存储对象,占用的空间会较大降低存储传输效率。Java序列化方式存储对象存储密度是很低的。Java生态系统中有挺多的序列化框架,例如:Kryo、Avro、ProtoBuf等。

Flink 摒弃了 Java 原生的序列化方法, 实现了自己的序列化框架,使用TypeInformation表示每种数据类型,所以可以只保存一份对象Schema信息,节省存储空间。又因为对象类型固定,所以可以通过偏移量存取。

  • 它支持很多种类的类型:基本类型、数组类型、复合类型、辅助类型、通用类型。
  • 对于我们创建的任意的一个POJO类型 (如Java Bean),在Java中,使用Class可以用来描述该类型。但其实在Flink引擎中,它被描述为PojoTypeInfo,而PojoTypeInfo是TypeInformation的子类。
  • TypeInformation是Flink类型系统的核心类。Flink使用TypeInformation来描述所有Flink支持的数据类型,就像Java中的Class类型一样。每一种Flink支持的数据类型都对应的是TypeInformation的子类。
  • 除了对类型地描述之外,TypeInformation还提供了序列化的支撑。在TypeInformation中有一个方法:createSerializer方法,
  • Flink中也提供了非常丰富的序列化器。

Flink是使用Java的序列化方式吗?

  • 不是。Flink自己实现了一套类型系统和序列化系统。

Java序列化方式有什么问题?

  • Java序列化方式对象存储很稀疏,除了存储数据本身外,还需要存储Class header数据、对齐填充数据。针对海量数据,会占用较多额外空间降低存储传输效率。

Java中是用Class描述类型,Flink也是用Class描述吗?

  • 不是。Flink是基于TypeInformation描述类型信息,针对每一种类型Flink都提供了自己的TypeInformation,例如:PojoTypeInfo、ValueTypeInfo等等。

请解释一下Java类型擦除。

  • Java中使用泛型只是在编译时进行强制类型检查,避免因为类型问题出现Runtime异常。而在运行过程中,泛型类型是会被擦除掉的。

Flink中为什么使用Lambda表达式实现flatMap需要通过returns指定类型呢?

  • flatMap有一个泛型参数Colletor,因为Java编译器类型擦除的原因,导致Flink在运行时是无法推断出来flatMap任务输出的类型信息,所以需要returns指定输出的类型。

new ArrayList()和new ArrayList(){}的区别是什么?

  • 使用起来是一样的。但后者是匿名内部类,在运行时会保留泛型信息。

9、Flink 部署模式

(1) local方式(本地测试用)

该方式是在Java虚拟机上运行Flink程序,或者是在正在运行程序的Java虚拟机上,像我们在IDE上直接运行就是采用的local方式,这种方式会获取到一个LocalExecutionEnvironment(或者CollectionEnvironment)类的环境上下文对象,默认并行度是当前可用处理器的Java虚拟机的数量

(1)Standalone 模式
配置一个或多个JobManager(HA模式),和一台或多台TaskManager,通过flink中bin下面的start-cluster.sh启动,关于这种方式的启动
(2)Yarn 模式

以 Yarn 模式部署 Flink 任务时,要求 Flink 是有 Hadoop 支持的版本,Hadoop 环境需要保证版本在 2.2以上,并且集群中安装有 HDFS 服务。

1)Session-Cluster 模式
在 yarn 中初始化一个 flink 集群,开辟指定的资源,以后提交任务都向这里提交。这个 flink 集群会常驻在 yarn 集群中,除非手工停止。

需要先启动集群,然后再提交作业,接着会向 yarn 申请一块空间后,资源永远保持不变。如果资源满了,下一个作业就无法提交,只能等到yarn中的其中一个作业执行完成后,释放了资源,下个作业才会正常提交。

  • 所有作业共享 Dispatcher 和 ResourceManager;共享资源;适合规模小执行时间短的作业。

任务启动步骤:

  1. 启动 hadoop 集群(略)
  2. 启动 yarn-session
  • ./yarn-session.sh -n 2 -s 2 -jm 1024 -tm 1024 -nm test -d 其中:
    -n(–container):TaskManager 的数量。
    -s(–slots): 每个 TaskManager 的 slot 数量,默认一个 slot 一个 core,默认每个taskmanager 的 slot 的个数为 1,有时可以多一些 taskmanager,做冗余。
    -jm:JobManager 的内存(单位 MB)。
    -tm:每个 taskmanager 的内存(单位 MB)。
    -nm:yarn 的 appName(现在 yarn 的 ui 上的名字)。
    -d:后台执行。
  1. 执行任务
    ./flink run -c com.atguigu.wc.StreamWordCount FlinkTutorial-1.0.jar --host lcoalhost –port 7777;
  2. 去 yarn 控制台查看任务状态
  3. 取消 yarn-session yarn application --kill application_1577588252906_0001

2) Per-Job-Cluster 模式:
每次提交都会创建一个新的 flink 集群,任务之间互相独立,互不影响,方便管理。任务执行完成之后创建的集群也会消失。

一个 Job 会对应一个集群,每提交一个作业会根据自身的情况,都会单独向 yarn申请资源,直到作业执行完成,一个作业的失败与否并不会影响下一个作业的正常 提交和运行。

  • 独享 Dispatcher 和 ResourceManager,按需接受资源申请;适合规模大 长时间运行的作业。

任务启动步骤:

  1. 启动 hadoop 集群(略)
  2. 不启动 yarn-session,直接执行 job
  • ./flink run –m yarn-cluster -c com.atguigu.wc.StreamWordCount FlinkTutorial-1.0.jar --host lcoalhost –port 7777
  • Yarn方式(生产环境用)
    flink的yarn模式部署也分为两种方式,一种是yarn-session,一种是yarn-per-job。大致如下图:
    在这里插入图片描述
模式提交语句要求
StandAlnoe模式flink run -c streaming.slot.lesson01.WordCount -p 2 flinklesson-1.0-SNAPSHOT.jar
Flink on Yarn Per-Job-Clusterflink run -m yarn-cluster -p 2 -yn 2 -yjm 1024 -ytm 1024 -c streaming.slot.lesson01.WordCount flinklesson-1.0-SNAPSHOT.jar 特点: 一个任务一个集群掌握
Flink on Yarn yarn-sessionyarn-session.sh -n 2 -jm 1024 -tm 1024
flink run ./examples/batch/WordCount.jar -input hdfs://hadoop100:9000/LICENSE -output hdfs://hadoop100:9000/wordcount-result.txt

各种模式总结

  • 1.Session Mode: 一个 session,然后会有多个程序提交到这一个 session 中。 好处:集群资源仅分配一次,充分利用资源,程序App 启动较快 坏处:可能会连锁式的重启,jobManager 负载大
  • 2.Per-Job Mode:使用的比较多,一个 application 一个 flink cluster 好处: 资源隔离,粒度更细,方便管理单个 job 坏处:当某个机器上有多个 client 时,会有较高的网络负载( 下载 jar 、传输 jar )以及消费大量的 CPU 来执行 main方法
  • 3.Application Mode:Application Mode 与 Per-Job Mode 类似,主要是为了解决 Per-Job Mode 的不足,通过 yarn.provided.lib.dirs 另外 client 是在 JobManager 上执行的,可以避免 带宽、CPU 的热点问题。 并且相比于 Per-Job Mode 来说,更强大,可以提交多个 job

Application Mode 与 Per-Job Mode 类似,它主要是为了解决 Per-Job Mode 中由于 client 端导致的 带宽、CPU 问题 Session Mode

11、flink 提交 job 的方式以及参数如何设置? 页面提交和客户端提交有什么区别?

向 Flink 集群提交任务,共有以下两种方式:

  • 使用Web页面Submit New Job方式提交
  • 使用命令行方式提交

1)使用Web页面Submit New Job方式提交
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
2、使用命令行方式提交

一、flink run参数

flink run -c streaming.slot.lesson01.WordCount -p 2 flinklesson-1.0-SNAPSHOT.jar

  • -c,–class : 需要指定的main方法的类
  • -p,–parallelism : job需要指定env的并行度,这个一般都需要设置。
  • -d,–detached : 在后台运行
  • -C,–classpath : 向每个用户代码添加url,他是通过UrlClassLoader加载。url需要指定文件的schema如(file://)
  • -q,–sysoutLogging : 禁止logging输出作为标准输出。
  • -s,–fromSavepoint : 基于savepoint保存下来的路径,进行恢复。
  • -sae,–shutdownOnAttachedExit : 如果是前台的方式提交,当客户端中断,集群执行的job任务也会shutdown。

二、flink run -m yarn-cluster参数
flink run -m yarn-cluster -p 2 -yn 2 -yjm 1024 -ytm 1024 -c streaming.slot.lesson01.WordCount flinklesson-1.0-SNAPSHOT.jar

  • -m,–jobmanager : yarn-cluster集群
  • -yd,–yarndetached : 后台
  • -yjm,–yarnjobManager : jobmanager的内存
  • -ytm,–yarntaskManager : taskmanager的内存
  • -yn,–yarncontainer : TaskManager的个数
  • -yid,–yarnapplicationId : job依附的applicationId
  • -ynm,–yarnname : application的名称
  • -ys,–yarnslots : 分配的slots个数

10、Flink 组件栈和数据流模型。

  • 1.DataStream API:对数据流进行流处理操作,将流式的数据抽象成分布式的数据流,支持 Java 和 Scala;
  • 2.DataSet API:对静态数据进行批处理操作,将静态数据抽象成分布式的数据集,支持 Java、Scala 和 Python;
  • 3.Table API:对结构化数据进行查询操作,将结构化数据抽象成关系表,并通过类 SQL 的 DSL 对关系表进行各种查询操作,支持 Java 和 Scala。
  • 4.Flink ML:提供了机器学习 Pipelines API 并实现了多种机器学习算法;Gelly、Flink 的图计算库提供了图计算的相关 API 及多种图计算算法的实现。

Flink 程序的基本构建是数据输入来自一个 Source,Source 代表数据的输入端,经过 Transformation 进行转换,然后在一个或者多个 Sink 接收器中结束。

数据流(Stream)就是一组永远不会停止的数据记录流,而转换(Transformation)是将一个或多个流作为输入,并生成一个或多个输出流的操作。在执行时,Flink 程序映射到 Streaming Dataflows,由流(Streams)和转换操作(Transformation Operators)组成。

11、Flink的CEP机制

CEP全称为Complex Event Processing,复杂事件处理

Flink CEP是在 Flink 中实现的复杂事件处理(CEP)库,CEP 允许在无休止的事件流中检测事件模式,让我们有机会掌握数据中重要的部分。一个或多个由简单事件构成的事件流通过一定的规则匹配,然后输出用户想得到的数据 —— 满足规则的复杂事件

12、 flink 集群规模?

公司自建机房:三个机架15台物理机:

  • 128G内存,20核40线程物理CPU,10T硬盘,单台报价4W出头。

  • 大数据服务器,7台,数据仓库,spark集群计算,flink集群计算,数据主要为hbase存储数据与保存到hdfs的日志数据,

  • CDH:国内使用最多的版本,但 CM不开源,但其实对中、小公司使用来说没有影响(建议使用)

  • 其他服务,5台,部署其他服务,数据库、ngix、消息系统mqtt集群,前端组态,后端Java程序运行代码等。

  • 测试服务器,3台

13、Flink消费kafka

Flink 提供了专门的 Kafka 连接器,向 Kafka topic 中读取或者写入数据。

原文链接

Flink中的Kafka消费者,集成了 Flink 的 Checkpoint 机制。触发checkpoint时, 每个分区的offset都存储在checkpoint中。 可以 实现exactly-once 的处理语义

消费模式是auto.offset.reset=eraliest

Flink消费一些数据并且提交了checkpoint, 如果发生故障,Flink将通过从checkpoint,加载状态后端,从上一次的checkpoint提交的offset处开始消费。,继续恢复应用程序,可以做到所谓的断点续传。

final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

env.enableCheckpointing(5000);
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
env.setParallelism(1);

Properties props = new Properties();
props.setProperty("bootstrap.servers",KAFKA_BROKER);
props.setProperty("zookeeper.connect", ZK_HOST);
props.setProperty("group.id",GROUP_ID);
props.setProperty("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.setProperty("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");

FlinkKafkaConsumer011<String> consumer = new FlinkKafkaConsumer011<>(TOPIC, new SimpleStringSchema(), props);

//方式一 :  默认:从topic中指定的group上次消费的位置开始消费,必须配置group.id参数
consumer.setStartFromGroupOffsets();

//方式二: 从topic中最初的数据开始消费
consumer.setStartFromEarliest();

//方式三: 从指定的时间戳开始
consumer.setStartFromTimestamp(1559801580000l);


//方式四: 从最新的数据开始消费
consumer.setStartFromLatest();

//方式五:Flink从topic中指定的offset开始,这个比较复杂,需要手动指定offset

Map<KafkaTopicPartition, Long> offsets = new HashedMap();
offsets.put(new KafkaTopicPartition("test", 0), 11111111l);
offsets.put(new KafkaTopicPartition("test", 1), 22222222l);
offsets.put(new KafkaTopicPartition("test", 2), 33333333l);

consumer.setStartFromSpecificOffsets(offsets);
/*
Map<KafkaTopicPartition, Long> Long参数,指定的offset位置
KafkaTopicPartition构造函数有两个参数,第一个为topic名字,第二个为分区数
获取offset信息,可以用过Kafka自带的kafka-consumer-groups.sh脚本获取
*/

一般使用,默认行为(setStartFromGroupOffsets):从topic中指定的group上次消费的位置开始消费。

必须配置group.id参数,从消费者组提交的偏移量开始读取分区 。

  • 如果,找不到分区的偏移量,auto.offset.reset将使用属性中的设置。
  • 如果,是,那么任务从检查点重启,按照重启前的offset进行消费,
  • 如果,直接重启不从检查点重启并且group.id不变,程序会按照上次提交的offset的位置继续消费。
  • 如果,group.id改变了,则程序按照auto.offset.reset设置的属性进行消费。但是如果程序带有状态的算子,还是建议使用检查点重启。

二、与其他框架的异同

1、Flink 和 Spark Streaming 的异同点有哪些?

  • 1、架构上:Flink 是实时处理引擎,基于事件驱动,会根据用户的代码处理成 Stream Graph,然后优化成为 JobGraph,与 Storm 形成的拓扑 Topology 结构类似。而 Spark Streaming 是微批(Micro-Batch)的模型,架构是基于 Spark,可以把 Spark Streaming 理解为时间维度上的 Spark DAG。
  • 2、时间机制:Spark Streaming只支持处理时间。 Flink支持处理时间、事件时间、注入时间。同时也支持watermark来处理滞后数据。
  • 3、容错机制:Spark Streaming 通过checkpoint实现数据不丢失,但无法做到恰好一次处理语义。Flink 则使用两阶段提交协议和checkpoint实现精准一次处理,容错性好。
  • 4、反压机制:Flink 在数据传输过程中使用了分布式阻塞队列;Spark Streaming 用到PID 算法,构造了一个速率控制器,任务的结束时间、处理时长、处理消息的条数。

2、 为什么要用Flink替代SparkStreaming?

Spark和Flink都具有流和批处理能力,但是他们的做法是相反的。

  • Spark Streaming是把流转化成一个个小的批来处理,这种方案的一个问题是我们需要的延迟越低, 额外开销占的比例就会越大,这导致了Spark Streaming很难做到秒级甚至亚秒级的延迟
  • Flink是把批当作一种有限的流,这种做法的一个特点是在流和批共享大部分代码的同时还能够保留批处理特有的一系列的优化。

同时,Flink相比于Spark而言还有诸多明显优势:

  • 支持高效容错的状态管理,保证在任何时间都能计算出正确的结果;
  • 同时支持高吞吐、低延迟、高性能的分布式流式数据处理框架;
  • 支持事件时间(Event Time)概念,事件即使无序到达甚至延迟到达,数据流都能够计算出精确的结果;
  • 轻量级分布式快照(Snapshot)实现的容错,能将计算过程分布到单台并行节点上进行处理。

3、Flink 的 checkpoint 机制与 spark 有什么不同

1)flink更轻量,可以根据时间戳更新state,

  • 轻量级的分布式快照,实现了每个操作符的快照,及循环流的在循环的数据的快照

  • 对比 Spark : 重量的快照,Spark每次全量的快照,

  • Flink 每次增量的快照,spark是每个批次全量保存

2)在Flink中的Checkpoint中,有精确一次概念和用法,而spark checkpoint没有仅一次的概念

3)flink的 checkpoint有三个状态后端:memery、rocksdb、hdfs;在Spark中checkpoint的存储位置一般保存在HDFS 与本地磁盘

4、Flink的key By和Spark的 group by有什么区别?

keyBy算子将DataStream转换成一个KeyedStream。KeyedStream是一种特殊的DataStream, 事实上,KeyedStream继承了DataStream,DataStream的各元素随机分布在各Task Slot中, KeyedStream的各元素按照Key分组,分配到各Task Slot中。

groupBy按照传入函数的返回值进行分组。将相同的key对应的值放入一个迭代器。

5、Flink 的运行必须依赖 Hadoop组件吗?

Flink可以完全独立于Hadoop,在不依赖Hadoop组件下运行。但是做为大数据的基础设施,Hadoop体系是任何大数据框架都绕不过去的。Flink可以集成众多Hadooop 组件,例如Yarn、Hbase、HDFS等等。例如,Flink可以和Yarn集成做资源调度,也可以读写HDFS,或者利用HDFS做检查点。

6、遇到Flink不太能解决的问题?(PV,UV放内存,OOM了,后面配合redis以及布隆过滤器)

有时候需要求uv ,内存或者状态中存过多数据,导致压力巨大, 这个时候可以结合Redis 或者 布隆过滤器来去重。

注意:布隆过滤器存在非常小的误判几率,不能判断某个元素一定百分之百存在,所以只能用在允许有少量误判的场景,不能用在需要100%精确判断存在的场景。

  1. 使用flink统计订单表的GMV,如果mysql中的数据出现错误,之后在mysql中做数据的修改操作,那么flink程序如何保证GMV的正确性,你们是如何解决?

一般有离线Job来恢复和完善实时数据。

三、架构

1、Flink架构

在这里插入图片描述
参考
Flink 运行时由两种类型的进程组成:一个 JobManager 和一个或者多个 TaskManager。

  • 1.Client:Flink 程序提交的客户端,用于准备数据流并将其发送给 JobManager。

  • 2.JobManager:管理节点, 主要负责 任务调度资源管理。包括,接收 Flink 作业job、调度 Task、收集作业状态、管理 TaskManager、协调 Task 做 checkpoint、Failover 故障恢复等

  • 3.TaskManager:计算节点,主要负责,具体任务执行、对应任务在每个节点上的资源申请和管理。 在启动的时候将资源的状态向 JobManager 汇报。

Flink集群启动后 , 会启动一个JobManger和一个或多个的TaskManager进程。TaskManager 在启动的时候就设置好了槽位数(Slot),每个 slot 能启动一个Task,Task 为线程。

1)Client
  • 客户端负责将任务提交到集群,与 JobManager 构建 Akka 连接,然后将任务提交到 JobManager,通过和 JobManager 之间进行交互获取任务执行状态。
  • 客户端提交任务可以采用 CLI 方式或者通过使用 Flink WebUI 提交,也可以在应用程序中指定 JobManager 的 RPC 网络端口构建ExecutionEnvironment 提交 Flink 应用。
2)JobManager

JobManager 相当于整个集群的 Master 节点,且整个集群有且只有一个活跃的 JobManager ,负责整个集群的任务管理和资源管理。

  • 从客户端中获取提交的应用,然后根据集群中 TaskManager 上 TaskSlot 的使用情况,为提交的应用分配相应的 TaskSlot 资源并命令 TaskManager 启动从客户端中获取的应用。

  • JobManager 和 TaskManager 之间通过 Actor System 进行通信,获取任务执行的情况并通过 Actor System 将应用的任务执行情况发送给客户端。

  • 在任务执行的过程中,Flink JobManager 会触发 Checkpoint 操作,每个 TaskManager 节点 收到 Checkpoint 触发指令后,完成 Checkpoint 操作,所有的 Checkpoint 协调过程都是在 Fink JobManager 中完成。

  • 当任务完成后,Flink 会将任务执行的信息反馈给客户端,并且释放掉 TaskManager 中的资源以供下一次提交任务使用。

JobManager 这个进程由三个不同的组件组成:

  • ResourceManager资源管理器
    负责 Flink 集群中的资源提供、回收、分配 、管理 task slots。Flink 为不同的环境和资源提供者(例如 YARN、Kubernetes 和 standalone 部署)实现了对应的 ResourceManager。在 standalone 设置中,ResourceManager 只能分配可用 TaskManager 的 slots,而不能自行启动新的 TaskManager。

  • Dispatcher分发器
    提供了一个 REST 接口,用来提交 Flink 应用程序执行,并为每个提交的作业启动一个新的 JobMaster。它还运行 Flink WebUI 用来提供作业执行信息。

  • JobMaster作业管理器
    JobMaster 负责管理单个JobGraph的执行。Flink 集群中可以同时运行多个作业,每个作业都有自己的 JobMaster。

3)TaskManager

TaskManager 相当于整个集群的 Slave 节点,负责具体的任务执行和对应任务在每个节点上的资源申请和管理。

  • 客户端通过将编写好的 Flink 应用编译打包,提交到 JobManager,然后 JobManager 会根据已注册在 JobManager 中 TaskManager 的资源情况,将任务分配给有资源的 TaskManager节点,然后启动并运行任务。
  • TaskManager 从 JobManager 接收需要部署的任务,然后使用 Slot 资源启动 Task,建立数据接入的网络连接,接收数据并开始数据处理。同时 TaskManager 之间的数据交互都是通过数据流的方式进行的。

Flink 的任务运行其实是采用多线程的方式,这和 MapReduce 多 JVM 进行的方式有很大的区别,Flink 能够极大提高 CPU 使用效率,在多个任务和 Task 之间通过 TaskSlot 方式共享系统资源,每个 TaskManager 中通过管理多个 TaskSlot 资源池进行对资源进行有效管理。

2、任务调度流程

Flink job:作业,类似Storm中的Topology,同样对应着Flink中的一层层的图,从StreamGraph --> JobGraph --> ExecutionGraph --> 物理执行图。

  • Task:类似于Storm中的Bolt,为了拓扑更高效地运行,Flink提出了Chaining,尽可能地将operators chain在一起作为一个task来处理。对应于JobGraph中的JobVertex。

  • operator:算子,对应于StreamGraph中的StreamNode。

在 Flink 中,一个 TaskManger 就是一个 JVM 进程,会用独立的线程来执行 Task。

任务调度流程:

  • 1)当用户提交一个 Flink 程序时,会先创建一个 Client,该 Client 首先会对用户提交的 Flink 程序代码进行预处理,获取 JobManager 的地址,并建立到 JobManager 的连接,将 Flink Job 提交给 JobManager
  • 2)JobManagerClient 处接收到 Job后,会生成优化后的执行计划,并以 Task 的单元调度到各个TaskManager 去执行。
  • 3)TaskManagerJobManager 处接收需要部署的 Task,部署启动后,与自己的上游建立 Netty 连接,接收数据并处理,并 将心跳和统计信息汇报给 JobManagerTaskManager 之间以流的形式进行数据的传输。

上述三者均为独立的 JVM 进程。

  • 1)当用户提交一个 Flink 程序时,会先创建一个 Client,该 Client 首先会对用户提交的 Flink 程序代码进行预处理,将 Stream API 编写的代码生成的最初的StreamGraph图,并经过优化后生成了JobGraph图
  • 2)同时,获取 JobManager 的地址,并建立到 JobManager 的连接,将 Flink JobGraph提交给 JobManager
  • 3)JobManagerClient 处接收到 Job后,根据 JobGraph 生成ExecutionGraph图,会生成优化后的物理执行图,并以 Task 的单元调度到各个TaskManager 去执行。
  • 4)TaskManagerJobManager 处接收需要部署的 Task,部署启动后,与自己的上游建立 Netty 连接,接收数据并处理,并 将心跳和统计信息汇报给 JobManagerTaskManager 之间以流的形式进行数据的传输。

注意:

  • Client 为提交 Job 的客户端,可以是运行在任何机器上(与 JobManager 环境连通即可)。提交 Job 后,Client 可以结束进程(Streaming 的任务),也可以不结束并等待结果返回。

  • 可以通过多种方式启动 JobManager 和 TaskManager:直接在机器上作为standalone 集群启动、在容器中启动、或者通过YARN等资源框架管理并启动。TaskManager 连接到 JobManagers,宣布自己可

  • 必须始终至少有一个 TaskManager。在 TaskManager 中资源调度的最小单位是 task slot。TaskManager 中 task slot 的数量表示并发处理 task 的数量。请注意一个 task slot 中可以执行多个算子

3、任务提交流程(YARN)

在这里插入图片描述

  • 1)ClientYarn ResourceManager提交任务,同时向HDFS上传Flink的Jar包和配置,以便后续启动 Flink 相关组件的容器。
  • 2)Yarn ResourceManager分配Container资源,并通知对应的Yarn NodeManager启动ApplicationMaster
  • 3)ApplicationMaster启动后加载Flink的Jar包和配置构建环境,然后启动JobManager,之后ApplicationMasterYarn ResourceManager申请资源, 分配资源后,再通知资源所在节点的Yarn NodeManager启动TaskManager
  • 4)NodeManager加载Flink的Jar包和配置构建环境,并启动TaskManager;TaskManager启动后向JobManager发送心跳包,并等待JobManager向其分配任务。

会话(Session)模式
在会话模式下,我们需要先启动一个YARN session,这个会话会创建一个 Flink 集群。 这里只启动了 JobManager,而 TaskManager 可以根据需要动态地启动。在 JobManager 内 部,由于还没有提交作业,所以只有 ResourceManager 和 Dispatcher 在运行,
(1)客户端通过 REST 接口,将作业提交给分发器。
(2)分发器启动 JobMaster,并将作业(包含 JobGraph)提交给 JobMaster。
(3)JobMaster 向资源管理器请求资源(slots)。
(4)资源管理器向 YARN 的资源管理器请求 container 资源。
(5)YARN 启动新的 TaskManager 容器。
(6)TaskManager 启动之后,向 Flink 的资源管理器注册自己的可用任务槽。
(7)资源管理器通知 TaskManager 为新的作业提供 slots。
(8)TaskManager 连接到对应的 JobMaster,提供 slots。
(9)JobMaster 将需要执行的任务分发给 TaskManager,执行任务。

单作业(Per-Job)模式
在单作业模式下,Flink 集群不会预先启动,而是在提交作业时,才启动新的 JobManager。

(1)客户端将作业提交给 YARN 的资源管理器,这一步中会同时将 Flink 的 Jar 包和配置上传到 HDFS,以便后续启动 Flink 相关组件的容器。
(2)YARN 的资源管理器分配 Container 资源,启动 Flink JobManager,并将作业提交给JobMaster。这里省略了 Dispatcher 组件。
(3)JobMaster 向资源管理器请求资源(slots)。
(4)资源管理器向 YARN 的资源管理器请求 container 资源。
(5)YARN 启动新的 TaskManager 容器。
(6)TaskManager 启动之后,向 Flink 的资源管理器注册自己的可用任务槽。
(7)资源管理器通知 TaskManager 为新的作业提供 slots。
(8)TaskManager 连接到对应的 JobMaster,提供 slots。
(9)JobMaster 将需要执行的任务分发给 TaskManager,执行任务。

可见,区别只在于 JobManager 的启动方式,以及省去了分发器。当第 2 步作业提交给JobMaster,之后的流程就与会话模式完全一样了。

4、TaskManger 与 Slots

TaskManager 相当于整个集群的 Slave 节点,负责具体的任务执行和对应任务在每个节点上的资源申请和管理
相比 JobManager而言,TaskManager 的启动流程较为简单,启动类入口为org.apache.flink.runtime.taskexecutor.TaskManagerRunner
启动过程中主要进行 Slot 资源的分配、RPC 服务的初始化,以及JobManager 进行通信等。

Flink 的任务运行,其实是采用多线程的方式,这和 MapReduce 多 JVM 并行的方式有很大的区别,

Flink 中每一个 worker(TaskManager)都是一个 JVM 进程,它可能会在独立的线程上执行一个或多个 subtask。为了控制一个 worker 能接收多少个 task,worker 通过 task slot 来进行控制(一个 worker 至少有一个 task slot。

在多个任务和 Task 之间,通过TaskSlot方式共享系统资源,每个 TaskManager 通过管理多个 TaskSlot 资源池,对资源进行有效管理。

(1)TaskSlot

Flink 中的计算资源通过 Task Slot 来定义。每个 task slot 代表了 TaskManager 的一个固定大小的资源子集。例如,一个拥有3个slot的 TaskManager,会将其管理的内存平均分成三分分给各个 slot

优点:

  • 1)内存优化,将资源 slot 化,意味着来自不同job的task不会为了内存而竞争,而是每个task都拥有一定数量的内存储备。需要注意的是,这里不会涉及到CPU的隔离,slot目前仅仅用来隔离task的内存。
    - 2)减少网络传输,运行在同一个 JVM 的 Task 可以共享 TCP 连接,以减少网络传输,在一定程度上提高了程序的运行效率,降低了资源消耗。
  • 通过调整 task slot 的数量,允许用户定义 subtask 之间如何互相隔离。如果一个TaskManager 一个slot,那将意味着每个 task group 运行在独立的 JVM 中(该 JVM可能是通过一个特定的容器启动的),而一个 TaskManager 多个 slot 意味着更多的subtask 可以共享同一个 JVM。而在同一个 JVM 进程中的 task 将共享 TCP 连接(基于多路复用)和心跳消息。它们也可能共享数据集和数据结构,因此这减少了每个task 的负载。

(2)共享Slot:SlotSharingGroup

所谓的共享Slot,就是指:不同operator下面的subTask,可以在同一个Task Slot中运行,即共享Slot。(这里是说,一个operator往往会因为并行度的原因,被分解成并行度个数的Task,并行执行)

在Storm中,supervisor下面是work,work中往往一个Executor执行一个Task。

而在Flink中,TaskManager下面是slot,相同的是Slot和Work都是一个JVM进程,不同的是TaskManager会对Slot进行资源分配。

优点:

  • Flink 集群所需的task slots数与job中最高的并行度一致。也就是说我们不需要再去计算一个程序总共会起多少个task了。
  • 更容易获得更充分的资源利用。如果没有slot共享,那么非密集型操作source/flatmap就会占用同密集型操作 keyAggregation/sink 一样多的资源。如果有slot共享,将基线的2个并行度增加到6个,能充分利用slot资源,同时保证每个TaskManager能平均分配到重的subtasks。

5、Flink 中的并行度设置

并行度,某一个算子被切分成多少个子任务。
Flink 本并行度的优先级依次是**:1算子级别 > 2环境级别 > 3客户端级别 > 4集群配置级别。**

Task Slot 是静态的概念,是指 TaskManager 具有的并发执行能力,可以通过参数taskmanager.numberOfTaskSlots 进行配置;

而并行度 parallelism 是动态概念,即 TaskManager 运行程序时实际使用的并发能力,可以通过参数 parallelism.default进行配置。

也就是说,假设一共有 3 个 TaskManager,每一个 TaskManager 中的分配 3 个TaskSlot,也就是每个 TaskManager 可以接收 3 个 task,一共 9 个 TaskSlot,如果我们设置 parallelism.default=1,即运行程序默认的并行度为 1,9 个 TaskSlot 只用了 1个,有 8 个空闲,因此,设置合适的并行度才能提高效率。

6 、Flink Graph(DAG)

在Flink中的执行图可以分成四层:StreamGraph -> JobGraph -> ExecutionGraph -> 物理执行图。

  • StreamGraph:是根据用户通过 Stream API 编写的代码生成的最初的图,表示程序的拓扑结构,在client中生成
  • JobGraph:StreamGraph经过优化后生成了 JobGraph,提交给 JobManager 的数据结构。在client中生成
    • 主要的优化为,将多个符合条件的节点, chain 在一起作为一个节点,这样可以减少数据在节点之间流动所需要的序列化/反序列化/传输消耗。
  • ExecutionGraph:JobManager 根据 JobGraph 生成ExecutionGraph。 在JobManager 中生成
    • ExecutionGraph是JobGraph的并行化版本,是调度层最核心的数据结构。
  • 物理执行图:JobManager 根据 ExecutionGraph 对 Job 进行调度后,在各个TaskManager 上部署 Task 后形成的“图”,并不是一个具体的数据结构。

7、Operator chains

概述

Operator chains,存在于Flink中StreamGraph --> JobGraph的过程中,将多个符合条件的StreamNode节点 chain在一起,作为一个JobNode节点的优化行为。(StreamNode就是Operator,因此称为Operator chains)

为了更高效地分布式执行,Flink会尽可能地将operator的subtask链接(chain)在一起形成task。每个task在一个线程中执行。将 Operators 链接成 Task 是非常有效的优化,它能减少:
①线程之间的切换;
②消息的序列化/反序列化;
③数据在缓冲区的交换;
④延迟的同时提高整体的吞吐量。

可以进行Operator chains的条件

  • 1)上下游并行度一致
  • 2)下游数据没有其他的输入
  • 3)上下游节点都在同一个soltgroup中,默认是一样的,如果不是,单独指定的算子资源,会独占TaskSolt
  • 4)没有keyed操作
  • 5)数据发送策略是forward
  • 6)用户没有禁用chain

四、DataSteam代码相关

在这里插入图片描述

1、 Environment相关

  • getExecutionEnvironment, 会根据查询运行的方式,决定返回什么样的运行环境,是最常用的一种创建执行环境的方式。
  • createLocalEnvironment,返回本地执行环境,需要在调用时指定默认的并行度。
  • createRemoteEnvironment,返回集群执行环境,将 Jar 提交到远程服务器。需要在调用时指定 JobManager的 IP 和端口号,并指定要在集群中运行的。

2、source相关

source是程序的数据源输入,你可以通过StreamExecutionEnvironment.addSource(sourceFunction)来为你的程序添加一个source。

flink提供了大量的已经实现好的source方法,你也可以自定义source

(1)通过实现sourceFunction接口来自定义无并行度的source

(2)通过实现ParallelSourceFunction 接口 or 继承RichParallelSourceFunction 来自定义有并行度的source

不过大多数情况下,我们使用自带的source即可。

获取source的方式(自带的)

  • 1)基于文件readTextFile(path):读取文本文件,文件遵循TextInputFormat 读取规则,逐行读取并返回。
  • 2)基于socketsocketTextStream:从socker中读取数据,元素可以通过一个分隔符切开。
  • 3)基于集合fromCollection(Collection),通过java 的collection集合创建一个数据流,集合中的所有元素必须是相同类型的。
  • 4)自定义数据源addSource 可以实现读取第三方数据源的数据
  • 5)系统内置提供了一批connectors,连接器会提供对应的source支持【kafka】

自带的connectors

3、 流的处理 (合并、分开、选择)

union 多流合并,类型一致
connect 两条流分别处理,类型可不一致,可共享状态

Union 与connect 区别:
1. Union 之前两个流的类型必须是一样,Connect 可以不一样,在之后的 coMap中再去调整成为一样的。
2. Connect 只能操作两个流,Union 可以操作多个。

join 相当于innerjoin
coGroup 实现左外连接,第一个流没有join上,也要输出

1)流分裂Split与选择Select

  • Split:根据某些特征把一个 DataStream 拆分成两个或者多个 DataStream。
  • Select:从一个 SplitStream 中获取一个或者多个DataStream。

在这里插入图片描述
2)流合并Connect

  • Connect:连接两个保持他们类型的数据流,两个数据流被 Connect 之后,只是被放在了一个同一个流中,内部依然保持各自的数据和形式不发生任何变化,两个流相互独立。
  • CoMap/CoFlatMap:作用于 ConnectedStreams 上,功能与 map
    和 flatMap 一样,对 ConnectedStreams 中的每一个 Stream 分别进行 map 和 flatMap处理
    在这里插入图片描述
    在这里插入图片描述
    3)流合并Union
  • Union:对两个或者两个以上的 DataStream 进行 union 操作,产生一个包含所有 DataStream 元素的新 DataStream。
    在这里插入图片描述

4、transform相关

  • 1)Map:输入一个元素,然后返回一个元素,中间可以做一些清洗转换等操作
    在这里插入图片描述

  • 2)FlatMap:输入一个元素,可以返回零个,一个或者多个元素
    在这里插入图片描述

  • 3)Filter:过滤函数,对传入的数据进行判断,符合条件的数据会被留下
    在这里插入图片描述

  • 4)keyby

在这里插入图片描述

  • 5)Reduce:对数据进行聚合操作,结合当前元素上一次reduce返回值,进行聚合操作,然后返回一个新的值。

在这里插入图片描述

  • 6)Rich Functions: RichMapFunction、RichFlatMapFunction、RichFilterFunction
    在这里插入图片描述

5、Sink

Flink 没有类似于 spark 中 foreach 方法,让用户进行迭代的操作。虽有对外的输出操作都要利用 Sink 完成。最后通过类似如下方式完成整个任务最终输出操作。

stream.addSink(new MySink(xxxx)) 

官方提供了一部分的框架的 sink。除此以外,需要用户自定义实现 sink。
在这里插入图片描述

6、窗口相关

1)滚动窗口
适用场景:适合做 BI 统计等(做每个时间段的聚合计算)。
在这里插入图片描述
2) 滑动窗口
适用场景:对最近一个时间段内的统计
第一参数:窗口大小; 第二个参数:滑动步长
每隔5秒,统计最近15秒的
在这里插入图片描述
3)window function

window function 定义了要对窗口中收集的数据做的计算操作,可以分为两类:

增量聚合函数:

  • 每条数据到来就进行计算,保持一个简单的状态。应用场景如求和。
  • ReduceFunction(.reduce)、AggregateFunction(.aggregate)

全窗口函数:

  • 先把窗口所有数据收集起来,等计算的时候遍历所有数据。应用场景如窗口内排序比增量聚合函数能取到更多的上下文信息,例如窗口信息,状态信息。
  • ProcessWindowFunction(.process)、WindowFunction(.apply)

其它可选API
.trigger() ——触发器,定义window什么时候关闭,触发计算并输出结果
.evitor()——移除器,定义移除某些数据的逻辑
.allowedLateness() —一允许处理迟到的数据
.sideOutputLateData() ——将迟到的数据放入侧输出流·
.getSideOutput() ——获取侧输出流

EventTime 的引入
在 Flink 的流式处理中,绝大部分的业务都会使用 eventTime,一般只在
eventTime 无法使用时,才会被迫使用 ProcessingTime 或IngestionTime。

如果要使用 EventTime,那么需要引入 EventTime 的时间属性,引入方式如下所示
在这里插入图片描述

7、watermark相关

在这里插入图片描述

1)AssignerWithPeriodicWatermarks,周期性的生成 watermark

系统会周期性的将 watermark 插入到流中(水位线也是一种特殊的事件!)。默认周期是 200 毫秒。可以使用

在这里插入图片描述

自定义一个周期性的时间戳抽取
在这里插入图片描述
在这里插入图片描述

getCurrentWatermark()方法。如果方法返回一个时间戳大于之前水位的时间戳,新的 watermark
会被插入到流中。这个检查保证了水位线是单调递增的。如果方法返回的时间戳小于等于之前水位的时间戳,则不会产生新的 watermark。

AscendingTimestampExtractor:知道数据流的时间戳是单调递增的
可以使用 AscendingTimestampExtractor,这个类会直接使用数据的时间戳生成 watermark
在这里插入图片描述

2)AssignerWithPunctuatedWatermarks,间断式地生成 watermark。

这种方式不是固定时间的,而是可以根据需要对每条数据进行筛选和处理。
在这里插入图片描述

8、ProcessFunction API(底层 API)

之前学习的转换算子是无法访问事件的时间戳信息和水位线信息的。而这
在一些应用场景下,极为重要。例如 MapFunction 这样的 map 转换算子就无法访问时间戳或者当前事件的事件时间。

DataStream API 提供了一系列的 Low-Level 转换算子。可以访问时间
戳、watermark 以及注册定时事件
。还可以输出特定的一些事件,例如超时事件等。

在这里插入图片描述
重点KeyedProcessFunction
所有的 Process Function 都继承自RichFunction 接口,所以都有 open()close()getRuntimeContext()等方法。而KeyedProcessFunction还额外提供了两个方法。

  • processElement(I value, Context ctx, Collector<O> out),结果将会放在 Collector 数据类型中输出。Context 可以访问元素的时间戳,元素的 key,以及 TimerService 时间服务。Context 还可以将结果输出到别的流(side outputs)。
  • onTimer(long timestamp, OnTimerContext ctx, Collector<O> out) ,是一个回调函数。当之前注册的定时器触发时调用。
1)TimerService 和 定时器(Timers)

当定时器 timer 触发时,会执行回调函数 onTimer()。注意定时器 timer 只能在keyed streams 上面使用。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2)侧输出流(SideOutput)

大部分的 DataStream API 的算子的输出是单一输出,也就是某种数据类型的流。除了 split 算子,可以将一条流分成多条流,这些流的数据类型也都相同。

process function 的 side outputs 功能可以产生多条流,并且这些流的数据类型可以不一样。一个 side output 可以定义为 OutputTag[X]对象,X 是输出流的数据类型。process function 可以通过 Context 对象发射一个事件到一个或者多个 side outputs。

需求:将温度值低于 30 度的数据输出到 side output。
在这里插入图片描述

3)状态保存

检测传感器的温度值,如果连续的两个温度差值超过 10 度,就输出报警

在这里插入图片描述
在这里插入图片描述

五、DataSteam代码相关

1、 source

基于文件,readTextFile(path)
基于集合,fromCollection(Collection)

2、transform

  • Map 、 FlatMap、Filter、Reduce
  • MapPartition:类似map,一次处理一个分区的数据【如果在进行map处理的时候需要获取第三方资源链接,建议使用MapPartition】,streamAPI没有
    在这里插入图片描述
  • Aggregate:sum、max、min等
  • Distinc:返回一个数据集中去重之后的元素,data.distinct()
  • Join:内连接
  • OuterJoin:外链接
  • Cross:获取两个数据集的笛卡尔积
  • Union:返回两个数据集的总和,数据类型需要一致
  • First-n:获取集合中的前N个元素
  • Sort Partition:在本地对数据集的所有分区进行排序,通过sortPartition()的链接调用来完成对多个字段的排序

五、 其他

1、数据倾斜问题?Flink 中的 Window 出现了数据倾斜,你有什么解决办法?

产生数据倾斜的原因主要有 2 个方面:

  • ①业务上有严重的数据热点,比如滴滴打车的订单数据中北京、上海等几个城市的订单量远远超过其他地区;
  • ②技术上大量使用了 KeyBy、GroupBy 等操作,错误使用了分组 Key,人为产生数据热点。

因此解决问题的思路也很清晰:

  • ①业务上要尽量避免热点 key 的设计,例如我们可以把北京、上海等热点城市分成不同的区域,并进行单独处理;
  • ②技术上出现热点时,要调整方案打散原来的 key,避免直接聚合;此外 Flink 还提供了大量的功能可以避免数据倾斜。

2、Flink 任务出现很高的延迟,你会如何入手解决类似问题?

  • 1.在 Flink 的后台任务管理中,可以看到 Flink 的哪个算子和 task 出现了反压;
  • 2.资源调优和算子调优,对作业中的并发数(Parallelism)、CPU(Core)、堆内存(Heap_memory)等参数进行调优;
    作业参数调优,并行度的设置、State 的设置、Checkpoint 的设置。

请谈谈你们是如何处理脏数据的?
这也是一个开放性的面试题,建议你结合自己的实际业务来谈。比如可以通过一个 fliter 算子将不符合规则的数据过滤出去。当然了,我们也可以在数据源头就将一些不合理的数据抛弃,不允许进入 Flink 系统参与计算。

3、 Flink 的“三层图”结构是什么意思?

这道题要求面试者掌握Flink 框架引擎划分执行计划的详细过程。
一个 Flink 任务的 DAG 生成计算图,大致经历以下3个过程。

  • 1.首先,StreamGraph的拓扑结构,最接近代码层面,主要由 StreamNode和 StreamEdge构成,其中 StreamNode对应着 Operator,它们通过 StreamEdge进行链接。
  • 2.其次,JobGraph,是能被Flink引擎识别的数据结构,由JobVertex、JobEdge和 IntermediateDataSet3个元素组成。我们可以把JobGraph形象地比喻为一个抽水系统,JobVertex是水泵,JobEdge是水管,而 IntermediateDataSet则是中间的蓄水池
  • 3.最后,ExecutionGraph,由 JobGraph 转换而来,包含了任务具体执行所需的内容,是最贴近底层实现的执行图。

7、谈谈 Flink 的 SQL 部分是如何实现的?

Table SQL 是 Flink 提供的高级 API 操作。Flink SQL 是 Flink 实时计算为简化计算模型,降低用户使用实时计算门槛而设计的一套符合标准 SQL 语义的开发语言。

Flink 把 SQL 的 解析、优化 和 执行**教给了Calcite。从图中可以看到,无论是批查询 SQL 还是流式查询 SQL,都会经过对应的转换器 Parser 转换成为节点树 SQLNode tree,然后生成逻辑执行计划 Logical Plan,逻辑执行计划在经过优化后生成真正可以执行的物理执行计划,交给 DataSet 或者 DataStream 的 API 去执行。

https://mp.weixin.qq.com/s/xRqrojjFITuhswtjNJo7OQ

1.面试题一:应用架构

问题:公司怎么提交的实时任务,有多少 Job Manager、Task Manager?

解答:

  1. 我们使用 yarn session 模式提交任务;另一种方式是每次提交都会创建一个新的 Flink 集群,为每一个 job 提供资源,任务之间互相独立,互不影响,方便管理。任务执行完成之后创建的集群也会消失。
    线上命令脚本如下:bin/yarn-session.sh -n 7 -s 8 -jm 3072 -tm 32768 -qu root.*.* -nm *-* -d
    其中申请 7 个 taskManager,每个 8 核,每个 taskmanager 有 32768M 内存。

  2. 集群默认只有一个 Job Manager。但为了防止单点故障,我们配置了高可用。
    对于 standlone 模式,我们公司一般配置一个主 Job Manager,两个备用 Job Manager,然后结合 ZooKeeper 的使用,来达到高可用;对于 yarn 模式,yarn 在Job Mananger 故障会自动进行重启,所以只需要一个,我们配置的最大重启次数是10 次

2.面试题二:压测和监控

问题:怎么做压力测试和监控?

解答:我们一般碰到的压力来自以下几个方面:

一,产生数据流的速度如果过快,而下游的算子消费不过来的话,会产生背压

背压的监控可以使用 Flink Web UI(localhost:8081) 来可视化监控 Metrics,一旦报警就能知道。一般情况下背压问题的产生可能是由于 sink 这个 操作符没有优化好,做一下优化就可以了。比如如果是写入 ElasticSearch, 那么可以改成批量写入,可以调大 ElasticSearch 队列的大小等等策略。

二,设置 watermark 的最大延迟时间这个参数,如果设置的过大,可能会造成内存的压力。

可以设置最大延迟时间小一些,然后把迟到元素发送到侧输出流中去。
晚一点更新结果。或者使用类似于 RocksDB 这样的状态后端, RocksDB 会开辟堆外存储空间,但 IO 速度会变慢,需要权衡。

三,还有就是滑动窗口的长度如果过长,而滑动距离很短的话,Flink 的性能
会下降的很厉害。

我们主要通过时间分片的方法,将每个元素只存入一个“重叠窗
口”,这样就可以减少窗口处理中状态的写入。参
见链接:
https://www.infoq.cn/article/sIhs_qY6HCpMQNblTI9M

四,状态后端使用 RocksDB,还没有碰到被撑爆的问题

3.面试题三:为什么用 Flink

问题:为什么使用 Flink 替代 Spark?
解答:主要考虑的是 flink 的低延迟、高吞吐量和对流式数据应用场景更好的支持;另外,flink 可以很好地处理乱序数据,而且可以保证 exactly-once 的状态一致性。详见文档第一章,有 Flink 和 Spark 的详细对比。

4.面试题四:checkpoint 的存储

问题:Flink 的 checkpoint 存在哪里?
解答:可以是内存,文件系统,或者 RocksDB。

5.面试题五:exactly-once 的保证

问题:如果下级存储不支持事务,Flink 怎么保证 exactly-once?

解答:端到端的 exactly-once 对 sink 要求比较高,具体实现主要有幂等写入和事务性写入两种方式。

幂等写入的场景依赖于业务逻辑,更常见的是用事务性写入。而事务性写入又有预写日志(WAL)和两阶段提交(2PC)两种方式。

如果外部系统不支持事务,那么可以用**预写日志的方式,**把结果数据先当成状态保存,然后在收到 checkpoint 完成的通知时,一次性写入 sink 系统。
参见文档 9.2、9.3 节及课件《Flink 的状态一致性》

6.面试题六:状态机制

问题:说一下 Flink 状态机制?

解答:Flink 内置的很多算子,包括源 source,数据存储 sink 都是有状态的。在Flink 中,状态始终与特定算子相关联。Flink 会以 checkpoint 的形式对各个任务的状态进行快照,用于保证故障恢复时的状态一致性。Flink 通过状态后端来管理状态和 checkpoint 的存储,状态后端可以有不同的配置选择。详见文档第九章。

7.面试题七:海量 key 去重

问题:怎么去重?考虑一个实时场景:双十一场景,滑动窗口长度为 1 小时,滑动距离为 10 秒钟,亿级用户,怎样计算 UV?

解答:使用类似于 scala 的 set 数据结构或者 redis 的 set 显然是不行的,
因为可能有上亿个 Key,内存放不下。所以可以考虑使用布隆过滤器(Bloom Filter)来去重。

8.面试题八:checkpoint 与 spark 比较

问题:Flink 的 checkpoint 机制对比 spark 有什么不同和优势?
解答:spark streaming 的 checkpoint 仅仅是针对 driver 的故障恢复做了数据和元数据的 checkpoint。而 flink 的 checkpoint 机制 要复杂了很多,它采用的是轻量级的分布式快照,实现了每个算子的快照,及流动中的数据的快照。 文章链接: https://cloud.tencent.com/developer/article/1189624

9.面试题九:watermark 机制

问题:请详细解释一下 Flink 的 Watermark 机制。

解答:Watermark 本质是 Flink 中衡量 EventTime 进展的一个机制,主要用来处理乱序数据。

10.面试题十:exactly-once 如何实现

问题:Flink 中 exactly-once 语义是如何实现的,状态是如何存储的?

解答:Flink 依靠 checkpoint 机制来实现 exactly-once 语义,如果要实现端到端的 exactly-once,还需要外部 source 和 sink 满足一定的条件。状态的存储通过状态后端来管理,Flink 中可以配置不同的状态后端。

11.面试题十一:CEP

问题:Flink CEP 编程中当状态没有到达的时候会将数据保存在哪里?
解答:在流式处理中,CEP 当然是要支持 EventTime 的,那么相对应的也要
支持数据的迟到现象,也就是 watermark 的处理逻辑。CEP 对未匹配成功的事件序列的处理,和迟到数据是类似的。在 Flink CEP 的处理逻辑中,状态没有满足的和迟到的数据,都会存储在一个 Map 数据结构中,也就是说,如果我们限定判断事件序列的时长为 5 分钟,那么内存中就会存储 5 分钟的数据,这在我看来,也是对内存的极大损伤之一。

12.面试题十二:三种时间语义

问题:Flink 三种时间语义是什么,分别说出应用场景?

解答:

  1. Event Time:这是实际应用最常见的时间语义,具体见文档第七章。
  2. Processing Time:没有事件时间的情况下,或者对实时性要求超高的情况下。
  3. Ingestion Time:存在多个 Source Operator 的情况下,每个 Source Operator可以使用自己本地系统时钟指派 Ingestion Time。后续基于时间相关的各种操作,都会使用数据记录中的 Ingestion Time。

13.面试题十三:数据高峰的处理

问题:Flink 程序在面对数据高峰期时如何处理?

解答:使用大容量的 Kafka 把数据先放到消息队列里面作为数据源,再使用
Flink 进行消费,不过这样会影响到一点实时性

  • 4
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值