2

原理

引入

批和流的区别:
批是有边界的流,流比批多了时间这个变量引入了更大的复杂性。

流处理系统设计目标:
流处理系统,需要解决好大数据处理的共性问题CAP,以及流处理的特有问题,满足正确性、实时性、高吞吐、灵活性的目标。

api的层级:
高层到底层:
SQL/Table API->DataStream/DataSet API->ProcessFunction 越来越具有表达性。
底层到高层:
ProcessFunction->DataStream/DataSet API->SQL/Table API 越来越简明易用。

flink作为一个分布式计算系统需要解决的问题有哪些?
1、分布式计算的问题
受CAP定理的约束,C和A是不可兼得的,于是在流式处理系统中,问题被定义成:在保证准确性的前提下,尽可能地追求实时性。
2、任务调度
master-slave架构进行任务调度正确管理和高效执行
3、资源管理
物理资源的抽象、管理、分配(任务如何获取)。
4、正确的语义
个人认为语义指的是业务逻辑正确。
遇到例外情况时能保证正确的语义(比如流处理中的乱序迟到)。
批的业务逻辑:批量的数据批量读入,分开处理,然后合并得出结果。
流的业务逻辑:源源不断地读入,源源不断地输出,要支持在输入数据乱序或者迟到的情况下,也能正确输出。
5、出错处理(容错)
批处理出错处理,重跑即可。
流处理数据源源不断,不存在“完整的数据集”:做法是存储中间状态,出错之后从存储的状态中恢复,并重放后续的数据。并且要做到轻量以保证系统的实时性和吞吐量。
6、流量控制
这点是对流处理来说,超出结点处理的上线,要启动流量控制以保护系统,避免引起雪崩效应。

应用场景:
一、Data Pipeline (管道)
实时数仓,实时搜索
二、 Data Analytics(分析)
Batch Analytics 和 Streaming Analytics
主要针对离线或者实时报表
三、 Data Driven(分析)
风控系统预警,处理各种各样复杂的规则

概念

  • 逻辑视图
    可以理解为用户定义的代码结构。
    定义中会使用到算子。
  • 算子
    算子(operator) 代表一个最顶级的 api 接口。
    使用算子构建数据流的逻辑视图。

    算子的一种分类:Source读取源,Transformation数据转换,Sink输出.

    一个算子在并行执行时,算子子任务会分布到多个节点上,所以算子子任务又被称为算子实例(Instance)。

    可以单独设置某个算子的并行度。
    类图:
    StreamOperator
      --生命周期方法
      --snapshort方法
    AbstractStreamOperator
      --设置和声明周期方法
    AbstractUdfStreamOperator
      --用户自定义函数的生命周期
      --快照策略

  • 算子的chain操作

需要shuffle的算子是chain的边界。
JobGraph生成时进行了算子chain优化。
类似于 Spark Streaming 中对于 Stage 的划分
image.png

  • 分区
    partition是指对数据流读取的并行切分。
    算子子任务与分区进行交互。

image.png
上图灰色部分代表分区,黄色部分代表算子。

  • 物理视图

逻辑视图只是一种抽象,需要将逻辑视图转化为物理执行图,才能在分布式环境下执行。

  • 数据交换策略

数据在不同的算子子任务上进行着数据交换。
前向传播(Forward):两个算子之间只有一个分区。
按Key分组(Key-Based):相同Key的数据会被发送到同一个分区上。一个分区上可以有多种key。
广播(Broadcast):将某份数据发送到所有分区上。
随机策略(Random):将所有数据随机均匀地发送到多个分区上,反之数据倾斜。

  • operator算子生命周期

参考 flink算子的生命周期

代码开发流程

flink是懒执行的,所以代码开发是作业定义的过程。

  1. 初始化运行环境。
    用户jar包所处的flink环境。
    通过flink静态方法工具类获取当前flink的上下文。
  2. 读取一到多个数据源Source。
    Source可以是消息队列或文件。
    flink提供常见的Source类型封装类。
  3. 根据业务逻辑对数据流进行Transformation转换。
    该步骤可以对数据流实现分组、窗口和聚合操作。
    DataStream可能被转换为KeyedStream、WindowedStream、JoinedStream等不同的数据流结构。
  4. 将结果输出到Sink。
    目的地可能是一个消息队列、文件系统或数据库。
  5. 调用作业执行函数。
    Flink是延迟执行(Lazy Evaluation)的,即当程序明确调用execute()方法时,Flink才会将数据流图转化为一个JobGraph,提交给JobManager。

二、代码提交方式
打成jar包

作业执行流程

架构

image.png
Flink On YARN

上图中的ResourceManager是YARN的 ResourceManager,不是Flink的ResourceManager。Flink的ResourceManager在ApplicationMaster中

image.png
Flink 架构

Flink系统由Flink Program、JobManager、TaskManager三个部分组成。

Flink Program: 加载代码,解析生成拓扑图,并将拓扑图提交给JobManager。该功能在CLINET提交前完成。

JobManager:拓扑图,生成物理执行计划,将执行计划发送给TaskManager执行。
JobManager还负责协调checkpoint的生成,从TaskManager收集Operator的状态, 周期性生成checkpoint。

TaskManager:管理任务的资源,执行具体任务,任务之间的传递。

三者使用Akka框架(Actor System)进行通信。

  • TaskManager
    TaskManager运行在一个JVM进程上。
    一个TaskManager上可以配置多个TaskSlot,一个TaskSlot运一个线程。
    TaskManager把内存分配个TaskSlot。

Flink 允许子任务共享 slot,即子任务合并运行在一个TaskSlot上有助于提高资源的利用效率。

流程

image.png
Flink 作业提交执行过程

1、生成执行图:
StreamGraph(用户代码对应的初始化图) -> JobGraph(对StreamGraph优化,相同的操作放在同一节点上) -> ExecutionGraph(根据上一步得来,是上一个图的并行化版本) -> 物理执行图(各个TaskManager 上部署 Task 后形成的“图”)

2、执行过程

不同的Task之间数据传输流程:
operator处理完成 -> RecordWriter -> ChannelSelector选择下游节点 -> 序列话写入ResultSubPartition -> flush吸入下游节点 -> 下游节点接收写入InputChannel -> 反序列化交给RecordReader -> 算子执行

Streaming 原理

Event Time

Time(时间)

时间是我们判断业务状态是否滞后,数据处理是否及时的重要依据。
有以下三个时间类型。

  1. Event time(事件时间)
    事件在其设备上发生的时间。
    数据的产生时间。
    在数据最源头产生时带有时间戳,后面都需要用时间戳来进行运算
  2. Ingestion time(注入时间)
    事件注入到 flink 的时间。

  3. Processing time(事件处理时间)
    flink机器处理事件的时间。

时间

window(窗口)

window是无界流数据处理的关键,flink将无界流拆分成无数个window。

一个窗口是有时间范围或数据量多少的。一种方式是可以按照时间界定窗口时间范围,另外一种方式是按照事件数量界定窗口内数据量的多少。

窗口类型

重叠的概念:
1、时间重叠,指的是两个或多个窗口之间在时间上有重复。
2、事件重叠,指的是两个或多个窗口之间在事件上有重复。

窗口分类:
1、滚动窗口(Tumbling Windows)
无重叠
2、滑动窗口(Sliding Windows)
有重叠
3、会话窗口(Session Windows)
无重叠,一段时间内没有接收到新数据就会就会生成新的窗口
4、全局窗口(Global Windows)

窗口类图:
图片描述

窗口生命周期

窗口何时开始?
1、时间窗口,定时时间开始,并且第一个元素达到时。
2、数量窗口,上一个窗口结束后,第一个元素到达。

窗口何时结束?
1、时间窗口,定时时间结束。
2、数量窗口,事件数量达到用户设置的数量。

窗口的声明代码

图片描述

watermark(水位线)

水位线是建立在window窗口之上的概念,狭义上指时间戳。

可以想象一副场景,数据在管道中流动,有一条线把数据流分成了两部分,水位线就是用来截断数据的那根线。

watermark与窗口

水位线是解决数据处理时效性的一种工具。

时间类窗口的目的就是为了收集周期内的事件,而事件偏偏是不完美的,即窗口内收的事件不在一个时间周期内。

水位线就是框架端提供了一种办法,可以把那些业务上认为太晚的事件给丢弃掉,这样保证了时效性,也增加了正确性,即时间窗口周期内处理的事件在eventTime维度上更加集中。

如果所有事件都能100%在eventTime时刻到达,也就是事件生产完就到达处理,直接用窗口就好了,不需要水位线。

水位线 = 窗口内所有数据最大的eventTime - 用户设置的阈值

watermark生成流程

1、在窗口内的所有数据事件时间eventTime中找到最大的时间Tmax。

2、用最大的时间Tmax减去用户设置的水位线阈值threshold,即得到watermark水位线时间。

3、使用watermark水位线时间过滤一遍数据:丢弃掉比水位线时间小的更老的事件丢弃掉,保留比水位线以上更新的数据。从而保证了数据处理在flink中的时效性

水位线阈值如何设置

往往按照业务需要或者经验。

比如我认为我已经收到了最新T1的数据,比T1更早的时间点T2以前的数据即使过来了也不需要处理了,那么阈值就可以设置为T1-T2。

watermark作用

用于对窗口迟到数据的处理,eventTime小于(早于)watermark的数据不再处理。

换种说法”watermark是用于处理乱序事件的“也是成立的。

watermark时间戳特点

本质上水位线就是一个时间戳,有以下两个特点:
1、动态:随着窗口一个接一个生成,watermark水位线的时间戳也是不断更新。
2、单调性:watermark不断更新,随着接收到的事件总体上越来越新,是一个比一个大(新)

State状态

Keyed State and Operator State

Operators算子

算子用来转换一个或者多个DataStream到一个新的DataStream

ProcessFunction

低级算子。
事件驱动具有状态的应用程序。
在事件(流元素)、状态(错误容忍,一致性,仅仅在keyed流上)、定时器(事件时间和处理时间,仅仅在keyed流上)

数据类型和内存管理

  • 类型描述器

TypeInformation类是所有类型描述类的基类。
子类对应着Flink支持的类型分类
子类有:BasicTypeInfo,IntegerTypeInfo,BasicArrayTypeInfo,CompositeType(子类有PojoTypeInfo->AvroTypeInfo, RowTypeInfo,TupleTypeInfo),ListTypeInfo,GenericTypeInfo。

  • 类型分类

基础类型: int Boolean,Date,String,BigDecimal
数组:Object[],基础类型数组
复合类型:Flink java tuple, scala tuple, ROW, POJO
辅助类型:Optional, Lists ,Maps
泛型和其它类型: 由kryo序列化

  • 类型提取

TypeExtractror
解决的问题:分析函数的输入和返回类型,获取类型信息(type information),从而获得序列化程序和反序列化程序工具

  • 类型暗示

解决的问题:自动推断函数的返回类型,帮助Flink的类型系统识别类型信息,从而实现序列。
使用TypeHint告诉Flink算子的运行时类型。
可以解决java泛型运行时擦除的问题,java编译后不保留泛型信息,是假泛型。
案例和解决:flatMap方法使用lambda表达式的返回类型是无法直接推断的,可以在flatMap之后加上 .returns(TypeInformation.of(WordWithCount.class));告诉flink返回的是什么类型;
如果返回本身有泛型可以增加TypeHint:.returns(TypeInformation.of(new TypeHint<Tuple2<String, String>>() { }))。

  • 类型定制

若用户有一些特殊的需求,只需要实现 TypeInformation、TypeSerializer 和 TypeComparator 即可定制自己类型的序列化和比较大小方式,来提升数据类型在序列化和比较时的性能。

  • 序列化

TypeSerializer
Flink自己实现了序列话框架。
相同结构数据集可以只保存一份对象Schema信息。
固定大小的类型,也可通过固定的偏移位置存取。
可以只对某个成员变量序列话。
用户自定义数据结果<-->序列化器<-->二进制<-->文件系统或者网络

image.png
上图表示Tuple3<Integer,Double,Person> 对象的序列化过程。
可以看出对值的序列话很紧凑。

  • Kryo 序列化

对于 Flink 无法序列化的类型(例如用户自定义类型,没有 registerType,也没有自定义 TypeInfo 和 TypeInfoFactory),默认会交给 Kryo 处理。

内存管理

flink实现了自己实现的内存管理,相比jvm的内存管理有很多优势:(jvm内存利用率低,存储密度低,大数据下GC停顿时间长)(flink优势在于减少GC,避免OOM,节约空间,二进制操作以及高效利用L1/2/3缓存)。

flink内存块MemorySegment:Flink 中最小的内存分配单元;底层可以Java 字节数组(byte[]),也可以是堆外的 ByteBuffer。
AbstractPagedInputView:用来组织MemorySegment的逻辑视图


jvm heap:
    1、保留个给用户代码和TaskManager数据结构
    2、Memory Manager Pool,默认占堆内存的70%,Flink 中的算法(sort/shuffle/join)向该内存池申请 MemorySegment,在Batch模式下使用
    3、Network Buffers

操作二进制

以排序为例:流程是
image.png
此图是排序前,该图右侧部分的详细图在下图中进行了展开说明。
pointer指针的含义是指向具体的数据位置。
binary fix-length sort key: 表示定长的排序前缀,从完整数据中截取的前面部分长度的数据。
image.png
此图是排序后

sort buffer 分成两块区域:1、二进制数据 2、数据指针和序列化的定长key(key+pointer)(不定长会取前缀)

实际的数据和(定长key+pointer)分开存放有两个目的:1、只用交换定长块(key+pointer)而不用交换数据更高效 2、定长对于CPU缓存友好,减少CPU cache miss

排序的关键是比大小和交换:1、比较的时候如果定长key相同,才需要把数据取出来比较,否则直接可以比较成功 2、交换key+pointer就可以达到排序的效果,真实的数据不用移动

磁盘IO和网络IO越来越快,CPU逐渐成为了大数据领域的瓶颈。
CPU时间中的时间浪费大部分在等待数据从主内存过来上。如果这些数据可以从 L1/L2/L3 缓存过来,那么这些等待时间可以极大地降低。

优点:交换数据量小,更高效(比如cpu缓存利用)

堆外内存

  • 为什么引入堆外内存

1、JVM在处理超大内存(几百G)时,启动慢,GC慢。堆外内存是没有这种问题的。
2、写磁盘或网络传输相比堆内内存更高效。相比堆内内存,堆外内存是zero-copy,不用copy。
3、由于堆外内存进程间共享,所以在JVM崩溃时堆外内存中数据不会丢失(但是flink现在没有利用起来这个特性)

  • 堆外内存的缺点

1、堆外内存相比堆内内存的调试监控更加不方便。
2、短生命周期的 MemorySegment,堆内内存相比堆外内存更廉价。
3、堆外内存相比堆内存上会慢一点点。

  • HeapMemorySegment

分配堆内存

  • HybridMemorySegment

可以分配堆外内存和堆内存

  • 实现以及JIT优化

TODO

事件处理CEP

(Complex Event Processing)
这是Apache Flink最亮眼的地方,相比于spark最大的优势。
CEP底层原理是NFA,非确定有穷自动机。

CEP库允许你在流上定义一系列的模式(pattern),可以是单个模式,也可以组合模式,还可以定义最终结果事件的忽略策略,以达到抽取业务关心的事件出来。
抽取事件可以用回调的方式来处理,还可以用新的流来输出。

基本概念

一、模式的属性(单个模式内)
1、条件属性
where/or/util
2、循环属性
times/oneMoreTimes/timesOrMore
3、可选属性
greedy/optional
4、有效期
within,需要确定多个模式之间是否也可以。

二、模式间的联系(联合模式)
1、严格连续性
next/notNext,两个事件紧挨着
2、宽松连续性
followBy/notFollowBy,两个事件前后顺序
3、非确定宽松连续
followByAny,两个事件前后顺序,但两个事件中间还可以加入两个事件中的后面事件,比宽松连续性更加宽松。

四、模式组
Groups of Pattern

五、CEP扩展:
1、Wating状态超时等待
2、动态注入更新

使用以及执行步骤

一、定义模式
使用条件规则等定义事件匹配模式。
这一步定义模式的名称,模式匹配的业务逻辑。
还可以组合多个模式。

二、模式与流关联并定义结果的回调逻辑
使用步骤一种定义的模式与数据流进行关联,并实现匹配回调逻辑。
定义处理匹配结果,这一步还可以对过期事件进行处理的定义。

三、新的输出流
第二部可以返回输出流,输出流可以接入到其它flink支持的组件。


参考

【1】Apache Flink 零基础入门(一):基础概念解析
【2】flink watermark介绍
【3】Structured Streaming 之 Watermark 解析(虽然是spark的,也建议看看)
【4】Flink 调试watermark & allowedLateness
【5】零基础学Flink:Window & Watermark
【6】Flink CEP
【7】Apache Flink 官方文档应用开发
【8】CEP 模式和忽略策略
【9】flink java api
【10】Flink原理、架构与实现Part2 - 原理与架构
【11】java泛型类型擦除及Flink类型暗示
【12】Flink 类型和序列化机制简介
【13】Flink内存管理- 如何直接操作二进制数据-堆外内存
【14】Flink原理与实践-皮皮鲁的科技星球
【15】一文搞懂Flink生成StreamGraph
【16】Flink任务提交 & 调度 & 相关概念 & Solt分配


肖圣贤
31 声望5 粉丝

学习让我充实