在当前数据量激增的时代,各种业务场景都有大量的业务数据产生,对于这些不断产生的数据应该如何进行有效的处理,成为当下大多数公司所面临的问题。随着雅虎对 Hadoop 的开源,越来越多的大数据处理技术开始涌入人们的视线,例如目前比较流行的大数据处理引擎 Apache Spark,。基本上已经取代了 MapReduce,成为当前大数据处理的标准。但随着数据的不断增长,新技术的不断发展,人们逐渐意识到对实时数据处理的重要性。相对于传统的数据处理模式,流式数据处理有着更高的处理效率和成本控制能力。Apache Fink 近年来逐步被人们所熟知,不仅是因为 Fink 提供同时支持高吞吐、低延迟和 exactly-once 语义的实时计算能力,同时 Flink 还提供了基于流式计算引擎处理批量数据的计算能力,真正意义上实现了批流统一。
在构建企业数据仓库的过程中,数据往往都是周期性的从业务系统中同步到大数据平台,完成一系列 ETL 转换动作之后,最终形成数据集市等应用。但是对于一些时间要求比较高的应用,例如实时报表统计,则必须有非常低的延时展示统计结果,为此业界提出一套 Lambda 架构方案来处理不同类型的数据。大数据平台中包含批量计算的 Batch Layer 和实时计算的 Speed Layer,通过在一套平台中将批计算和流计算整合在一起,例如使用 Hadoop MapReduce 进行批量数据的处理,使用 Apache Storm 进行实时数据的处理。这种架构在一定程度上解决了不同计算类型的问题,但是带来的问题是框架太多会导致平台复杂度过高、运维成本高等。在一套资源管理平台中管理不同类型的计算框架使用也是非常困难的事情。后来随着 Apache Spark 的分布式内存处理框架的出现,提出了将数据切分成微批的处理模式进行流式数据处理,从而能够在一套计算框架内完成批量计算和流式计算。但因为 Spak 本身是基于批处理模式的原因,并不能完美且高效地处理原生的数据流,因此对流式计算支持的相对较弱,可以说 Spak 的出现本质上是在一定程度上对 Hadoop 架构进行了一定的升级和优化。而有状态流计算架构的提出,从一定程度上满足了企业的需求,企业基于实时的流式数据,维护所有计算过程的状态,所谓状态就是计算过程中产生的中间计算结果,每次计算新的数据进入到流式系统中都是基于中间状态结果的基础上进行运算,最终产生正确的统计结果。基于有状态计算的方式最大的优势是不需要将原始数据重新从外部存储中拿出来,从而进行全量计算,因为这种计算方式的代价可能是非常高的。Apache Flink就是有状态流计算架构。
Fink优势
1、同时支持高吞吐、低延迟、高性能
2、支持事件时间(Event Time)概念
在流式计算领域中,窗口计算的地位举足轻重,但目前大多数框架窗口计算采用的都是系统时间(Process Time.),也是事件传输到计算框架处理时,系统主机的当前时间。Flinki 能够支持基于事件时间(Event Time)语义进行窗口计算,也就是使用事件产生的时间,这种基于事件驱动的机制使得事件即使乱序到达,流系统也能够计算出精确的结果,保持了事件原本产生时的时序性,尽可能避免网络传输或硬件系统的影响。
3、支持有状态计算
4、支特高度灵活的窗口(Vindow)操作
在流处理应用中,数据是连续不断的,需要通过窗口的方式对流数据进行一定范围的聚合计算,例如统计在过去的 1 分钟内有多少用户点击某一网页,在这种情况下,我们必须定义一个窗口,用来收集最近一分钟内的数据,并对这个窗口内的数据进行再计算。Flink 将窗口划分为基于 Time、Count、Session,以及 Data-driven 等类型的窗口操作,窗口可以用灵活的触发条件定制化来达到对复杂的流传输模式的支持,用户可以定义不同的窗口触发机制来满足不同的需求。
5、基于轻量级分布式快照(Snapshot)实现的容错
通过基于分布式快照技术的 Checkpoints,将执行过程中的状态信息进行持久化存储,一旦任务出现异常停止,Flink 就能够从 Checkpoints!中进行任务的自动恢复,以确保数据在处理过程中的一致性。
6、基于 JVM 实现独立的内存管理
7、Save Points(保存点)
Flink 通过 Save Points 技术将任务执行的快照保存在存储介质上,当任务重启的时候可以直接从事先保存的 Save Points 恢复原有的计算状态,使得任务继续按照停机之前的状态运行,Save Points 技术可以让用户更好地管理和运维实时流式应用。
Flink架构
整个 Flink 的架构体系基本上可以分为三层,由上往下依次是 APl&Libraries。层、Runtime 核心层以及物理部署层。
1、APl&Libraries层
作为分布式数据处理框架,FIik 同时提供了支撑流计算和批计算的接口,同时在此基础之上抽象出不同的应用类型的组件库,如基于流处理的 CEP(复杂事件处理库)、SQL&Table 库和基于批处理的 FlinkML(机器学习库)等、Gelly(图处理库)等。API 层包括构建流计算应用的 DataStream APl 和批计算应用的 DataSet API,。两者都提供给用户丰富的数据处理高级 APl,例如 Map、FlatMap 操作等,同时也提供比较低级的 Process Function APl,用户可以直接操作状态和时间等底层数据。
2、Runtime 核心心层
该层主要负责对上层不同接口提供基础服务,也是 Flink 分布式计算框架的核心实现层,支持分布式 Stream 作业的执行、JobGraph 到 ExecutionGraphl 的映射转换、任务调度等。将 DataSteam 和 DataSet 转成统一的可执行的 Task Operator,达到在流式引擎下同时处理批量计算和流式计算的目的。
3、物理部署层
该层主要涉及 Flink 的部署模式,目前 Flink, 支持多种部署模式:本地、集群 (Standalone/YARN)、云(GCE/EC2)、Kubenetes。Flink 能够通过该层能够支持不同平台的部署,用户可以根据需要选择使用对应的部署模式。
Flink整个系统主要由两个组件组成,分别为 JobManager (负责整个集群的调度和资源管理)和 TaskManager(负责具体的任务执行和每个Task在每个节点上的资源申请和管理)。 Flink 架构也遵循 Master-Slave 架构设计原则,JobManager 为 Master节点,TaskManager 为 Worker (Slave)节点。所有组件之间的通信都是借助于 Akka Framework,包括任务的状态以及 Checkpoint 触发等信息。
三、Flink编程模型
根据现实的数据产生方式和数据产生是否含有边界(具有起始点和终止点)角度,将数据分为两种类型的数据集,一种是有界数据集,另外一种是无界数据集。
有界数据集具有时间边界,在处理过程中数据一定会在某个时间范围内起始和结束,有可能是一分钟,也有可能是一天内的交易数据。对有界数据集的数据处理方式被称为批计算(Batch Processing),例如将数据从RDBMS或文件系统等系统中读取出来,然后在分布式系统内处理,最后再将处理结果写入存储介质中,整个过程就被称为批处理过程。而针对批数据处理,目前业界比较流行的分布式批处理框架有Apache Hadoop:和Apache Spark等。
对于无界数据集,数据从开始生成就一直持续不断地产生新的数据,因此数据是没有边界的,例如服务器的日志、传感器信号数据等。和批量数据处理方式对应,对无界数据集的数据处理方式被称为流式数据处理,简称为流处理。
四、DataStream编程模型
在Fink整个系统架构中,对流计算的支持是其最重要的功能之一,Flink基于Google提出的DataFlow模型,实现了支持原生数据流处理的计算引擎。Flink中定义了DataStream APl让用户灵活且高效地编写Flink流式应用。DataStream APl主要可为分为三个部分,DataSource模块、Transformation模块以及DataSink模块,其中Sources模块主要定义了数据接入功能,主要是将各种外部数据接入至Flink系统中,并将接入数据转换成对应的DataStream数据集。在Transformation模块定义了对DataStream数据集的各种转换操作,例如进行map、lter、windows等操作。最后,将结果数据通过DataSink模块写出到外部存储介质中,例如将数据输出到文件或Kafka消息中间件等。
DataSources:模块定义了DataStream API中的数据输入操作,Flink将数据源主要分为的内置数据源和第三方数据源这两种类型。其中内置数据源包含文件、Socket网络端口以及集合类型数据,其不需要引入其他依赖库,且在Fik系统内部已经实现,用户可以直接调用相关方法使用。第三方数据源定义了Fik和外部系统数据交互的逻辑,包括数据的读写接口。在Fik中定义了非常丰富的第三方数据源连接器(Connector),例如Apache kafka Connector、,Elatic Search
Connector等。同时用户也可以自定义实现Flink中数据接入函数SourceFunction,并封装成第三方数据源的Connector,完成Flink与其他外部系统的数据交互。
Flink通过实现SourceFunction定义了非常丰富的第三方数据连接器,基本覆盖了大部分的高性能存储介质以及中间件等,其中部分连接器是仅支持读取数据,例如Twitter Streaming API、Netty等;另外一部分仅支持数据输出(Sink),不支持数据输入(Source),例如Apache Cassandra、Elasticsearch、Hadoop FileSystem等。还有一部分是既支持数据输入,也支持数据输出,例如Apache Kafka、Amazon Kinesis、RabbitMQ等连接器。
DataStreaml的转换操作可分为单Single-DataStream、Multi一DaataStream、物理分区三类类型。其中Single一DataStream:操作定义了对单个DataStream数据集元素的处理逻辑,Muti-DataStream操作定义了对多个DataStream数据集元素的处理逻辑。物理分区定义了对数据集中的并行度和数据分区调整转换的处理逻辑。
物理分区(Physical Partitioning)操作的作用是根据指定的分区策略将数据重新分配到不同节点的Task案例上执行。当使用DataStream提供的API对数据处理过程中,依赖于算子本身对数据的分区控制,如果用户希望自己控制数据分区,例如当数据发生了数据倾斜的时候,就需要通过定义物理分区策略的方式对数据集进行重新分布处理。Flik中已经提供了常见的分区策略,例如随机分区(Random Partitioning)、平衡分区(Roundobin Partitioning)、按比例分区(Roundrobin Partitioning)等。当然如果给定的分区策略无法满足需求,也可以根据Fik提供的分区控制接口创建分区器,实现自定义分区控制。
时间概念类型
对于流式数据处理,最大的特点是数据上具有时间的属性特征,Fink根据时间产生的位置不同,将时间区分为三种时间概念,分别为事件生成时间(Event Time)、事件接入时间(Ingestion Time)和事件处理时间(Processing
Time)。如图4-7所示,数据从终端产生,或者从系统中产生的过程中生成的时间为事件生成时间,当数据经过消息中间件传入到Flink系统中,在DataSource中接入的时候会生成事件接入时间,当数据在Fik系统中通过各个算子实例执行转
换操作的过程中,算子实例所在系统的时间为数据处理时间。Fik已经支持这三种类型时间概念,用户能够根据需要选择时间类型作为对流式数据的依据,这种情况极大地增强了对事件数据处理的灵活性和准确性。
通常情况下,由于网络或系统等外部因素影响,事件数据往往不能及时传输至Fik系统中,导致数据乱序到达或者延迟到达等问题,因此,需要有一种机制能够控制数据处理的过程和进度,比如基于事件时间的Window创建后,具体该如何确定属于该Windowl的数据元素已经全部到达。如果确定全部到达,就可以对Windowl的所有数据做窗口计算操作(如汇总、分组等),如果数据没有全部到达,则继续等待该窗口中的数据全部到达才开始处理。这种情况下就需要用到水位线(VaterMarks)机制,它能够衡量数据处理进度(表达数据到达的完整性),保证事件数据(全部)到达Fik系统,或者在乱序及延迟到达时,也能够像预期一样计算出正确并且连续的结果。Flik会将用读取进入系统的最新事件时间减去固定的时间间隔作为Watermark,该时间间隔为用户外部配置的支持最大延迟到达的时间长度,也就是说理论上认为不会有事件超过该间隔到达,否则就认为是迟到事件或异常事件。
时间窗口概念
在Flink流式计算中,通过Vindows Assigner将接入数据分配到不同的窗口,根据Windows Assigner数据分配方式的不同将Windows分为4大类,分别是滚动窗口(Tumbling Windows)、滑动窗口(Sliding Windows)、会话窗口(Session Windows),和全局窗口(Global Windows)。并且这些Windows Assigneri已经在Flink中实现,用户调用DataStream APl的windows:或windowsAll方法来指定Windows Assigner即可。
1、滚动窗口是根据固定时间或大小进行切分,且窗口和窗口之间的元素互不重叠。这种类型的窗口的最大特点是比较简单,但可能会导致某些有前后关系的数据计算结果不正确,而对于按照固定大小和周期统计某一指标的
这种类型的窗口计算就比较适合,同时实现起来也比较方便。
2、滑动窗口也是一种比较常见的窗口类型,其特点是在滚动窗口基础之上增加了窗口滑动时间(Slide Time),且允许窗口数据发生重叠。如图4-12所示,当Windows size固定之后,窗口并不像滚动窗口按照Vindows Size向前移动,而是根据设定的Slide Time向前滑动。窗口之间的数据重叠大小根据Windows size和Slide time决定,当Slide time小于Vindows size便会发生窗口重叠,Slide size大于Windows size就会出现窗口不连续,数据可能不能在任何一个窗口内计算,Slide size和Windows size相等时,Sliding Windows:其实就是TumblingVindows。滑动窗口能够帮助用户根据设定的统计频率计算指定窗口大小的统计指标,例如每隔30s统计最近10min内活跃用户数等。
3、会话窗口(Session Windows)主要是将某段时间内活跃度较高的数据聚合成一个窗口进行计算,窗口的触发的条件是Session Gap,是指在规定的时间内如果没有数据活跃接入,则认为窗口结束,然后触发窗口计算结果。需要注意的是如果数据一直不间断地进入窗口,也会导致窗口始终不触发的情况。与滑动窗口、滚动窗口不同的是,Session Windows不需要有固定windows size:和slidetime,只需要定义session gap,来规定不活跃数据的时间上限即可。如图4-13所示,通过session gap.来判断数据是否属于同一活跃数据集,从而将数据切分成不同的窗口进行计算。
4、全局窗口(Global Windows)将所有相同的key的数据分配到单个窗口中计算结果,窗口没有起始和结束时间,窗口需要借助于Triger来触发计算,如果不对Global Windows:指定Triger,窗口是不会触发计算的。因此,使用GlobalWindows需要非常慎重,用户需要非常明确自己在整个窗口中统计出的结果是什么,并指定对应的触发器,同时还需要有指定相应的数据清理机制,否则数据将一直留在内存中。
延迟数据处理
基于Event一Time的窗口处理流式数据,虽然提供了Watermark机制,却只能在一定程度上解决了数据乱序的问题。但在某些情况下数据可能延时会非常严
重,即使通过Vatermark机制也无法等到数据全部进入窗口再进行处理。Flink中默认会将这些迟到的数据做丢弃处理,但是有些时候用户希望即使数据延迟到达
的情况下,也能够正常按照流程处理并输出结果,此时就需要使用AllowedLateness机制来对迟到的数据进行额外的处理。
DataStream API中提供了allowedLateness方法来指定是否对迟到数据进行处理,在该方法中传入Tme类型的时间间隔大小(t),其代表允许延时的最大时间,Flink窗口计算过程中会将Window的Endtime加上该时间,作为窗口最后被释放的结束时间(P),当接入的数据中Event Time未超过该时间(P),但Vatermak已经超过Vindow的EndTime时直接触发窗口计算。相反,如果事件时间超过了最大延时时间(P),则只能对数据进行丢弃处理。
作业链和资源组
在Fik作业中,用户可以指定相应的链条将相关性非常强的转换操作绑定在一起,这样能够让转换过程上下游的Task在同一个Pipeline中执行,进而避免因为数据在网络或者线程间传输导致的开销。一般情况下Flink在例如Map类型的操作中默认开启TaskChain,以提高Flink作业的整体性能。Flink同时也为用户提供细粒度的链条控制,用户能够根据自己的需要创建作业链或禁止作业链。
五、Flink状态管理和容错
直cink中Keyed State和Operator State均具有两种形式,其中一种为托管状态(Managed State)形式,由Flink Runtime中控制和管理状态数据,并将状态数据转换成为内存Hash tables:或RocksDB的对象存储,然后将这些状态数据通过内部的接口持久化到Checkpoints中,任务异常时可以通过这些状态数据恢复任务。另外一种是原生状态(Raw State)形式,由算子自己管理数据结构,当触发Checkpointi过程中,Flink并不知道状态数据内部的数据结构,只是将数据转换成oytes数据存储在Checkpoints中,当从Checkpoints'恢复任务时,算子自己再反序列化出状态的数据结构。DataStream API3支持使用Managed State和Raw Stater两种状态形式,在Flink中推荐用户使用Managed State管理状态数据,主要原因是Managed State能够更好地支持状态数据的重平衡以及更加完善的内存管理。
Flink中基于异步轻量级的分布式快照技术提供了Checkpoints容错机制,分布式快照可以将同一时间点Task/Operator的状态数据全局统一快照处理,包括前面提到的Keyed State和Operator State。.如图5-2所示,Flink会在输入的数据集上间隔性地生成checkpoint barrier,通过栅栏(barrier.)将间隔时间段内的数据划分到相应的checkpoint!中。当应用出现异常时,Operator就能够从上一次快照中恢复所有算子之前的状态,从而保证数据的一致性。例如在KafkaConsumer算子中维护Offset》状态,当系统出现问题无法从afka中消费数据时,可以将Offset记录在状态中,当任务重新恢复时就能够从指定的偏移量开始消费数据。对于状态占用空间比较小的应用,快照产生过程非常轻量,高频率创建且对Fik任务性能影响相对较小。checkpoint过程中状态数据一般被保存在一个可配置的环境中,通常是在JobManageri节点或HDFS上。
(1)Checkpoint开启和时间间隔指定
开启检查点并且指定检查点时间间隔为1000s,根据实际情况自行选择,如果状态比较大,则建议适当增加该值。
(2)exactly-ance和at-least一once语义选择
可以选择exactly一once语义保证整个应用内端到端的数据一致性,这种情况比较适合于数据要求比较高,不允许出现丢数据或者数据重复,与此同时,Flink的性能也相对较弱,而at-least一once语义更适合于时廷和吞吐量要求非常高但对数据的一致性要求不高的场景。
(3)Checkpoint超时时间
超时时间指定了每次Checkpoint执行过程中的上限时间范围,一旦Checkpoint执行时间超过该阈值,Flink将会中断Checkpoint过程,并按照超时处理。该指标可以通过setCheckpointTimeout方法设定,默认为l0分钟。
(4)检查点之间最小时间间隔
该参数主要目的是设定两个Checkpoint之间的最小时间间隔,防止出现例如状态数据过大而导致Checkpoint执行时间过长,从而导致Checkpoint积压过多,最终Flink应用密集地触发Checkpoint操作,会占用了大量计算资源而影响到整个
(5)最大并行执行的检查点数量
通过setMaxConcurrentCheckpoints()方法设定能够最大同时执行的Checkpoint数量。在默认情况下只有一个检查点可以运行,根据用户指定的数量可以同时触发多个Checkpoint,进而提升Checkpoint整体的效率。
(6)外部检查点
设定周期性的外部检查点,然后将状态数据持久化到外部系统中,使用这种方式不会在任务正常停止的过程中清理掉检查点数据,而是会一直保存在外部系统介质中,另外也可以通过从外部检查点中对任务进行恢复。
(7) failOnCheckpointingErrors
failOnCheckpointingErrors参数决定了当Checkpoint执行过程中如果出现失败或者错误时,任务是否同时被关闭,默认值为True。
Savepoints是检查点的一种特殊实现,底层其实也是使用Checkpoints的机制。Savepoints是用户 以手工命令的方式触发Checkpoint,并将结果持久化到指定的存储路径中,其主要目的是帮助用户在升级和维护集群过程中保存系统中的
状态数据,避免因为停机运维或者升级应用等正常终止应用的操作而导致系统无法恢复到原有的计算状态的情况,从而无法实现端到端的Excatly- -Once语义保证。
Flink中一共实现了三种类型的状态管理器,包括基于内存的MemoryStateBackend、基于文件系统的FsStateBackend,以及基于RockDB作为存储介质的RocksDBState- -Backend. 这三种类型的StateBackend都能够有效地存储Flink流式计算过程中产生的状态数据,在默认情况下Flink使用的是内存作为状态管理器。
迭代计算
迭代计算在批量数据处理过程中的应用非常广泛,如常用的机器学习算法KMeans、逻辑回归,以及图计算等,都会用到迭代计算。DataSet API对迭代计算功能的支持相对比较完善,在性能上较其他分布式计算框架也具有非常高的优势。目前Flink中的迭代计算种类有两种模式,分别是Bulk Iteration (全量迭代计算)和Delt Iteration (增量迭代计算)。
全量迭代计算过程在数据接入迭代算子过程中,Step Function每次都会处理全量的数据,然后计算下一次迭代的输入,也就是Next Partial Solution,最后根据触发条件输出迭代计算的结果,并将结果通过DataSet API传输到下一个算子中继续进行计算。Flink中迭代的数据和其他计算框架相比,并不是通过在迭代计算过程中不断生成新的数据集完成,而是基于同一份数据集上完成迭代计算操作,因此不需要对数据集进行大量拷贝复制操作,从而避免了数据在复制过程中所导致的性能下降问题。
增量迭代是通过部分计算取代全量计算,在计算过程中会将数据集分为热点数据和非热点数据集,每次迭代计算会针对热点数据展开,这种模式适合用于数据量比较大的计算场景,不需要对全部的输入数据集进行计算,所以在性能和速度上都会有很大的提升。
广播变量是分布式计算框架中经常会用到的一种数据共享方式,目的是对小数据集采用网络传输的方式,在每个并行的计算节点的实例内存中存储一份该数据集,所在的计算节点实例均可以在本地内存中直接读取被广播的数据集,这样
能够避免在数据计算过程中多次通过远程的方式从其他节点中读取小数据集,从而提升整体任务的计算性能。
在批计算中,需要处理的数据集大部分来自于文件,对于某些文件尽管是放在类似于HDFS之上的分布式文件系统中,但由于Flink并不像MapReduce-样让计算随着数据所在位置上进行,因此多数情况下会出现通过网络频繁地复制文件的情况。因此对于有些高频使用的文件可以通过分布式缓存的方式,将其放置在每台计算节点实例的本地task内存中,这样就能够避免因为读取某些文件而必须通过网络远程获取文件的情况,进而提升整个任务的执行效率。
在Flink批量数据处理过程中,往往传入函数的对象中可能含有很多字段,其中有些字段是Function计算会用到的,但有些字段在进入Funciton后并没有参与到实际计算过程中。针对这种情况,Flink提出 了语义注解的功能,将这些字段在Function中通过注解的形式标记出来,区分出哪些是需要参与函数计算的字段,哪些是直接输出的字段。Flink Runtime在执行算子过程中,会对注解的字段进行判别,对于不需要函数处理的字段直接转发到Output对象中,以减少数据传输过程中所消耗的网络IO或者不必要的排序操作等,以提升整体应用的处理效率。
七、Table APi和Flink SQL
对于像DataFrame这样的关系型编程接口,因其强大且灵活的表达能力,能够让用户通过非常丰富的接口对数据进行处理,有效降低了用户的使用成本,近年来逐渐成为主流大数据处理框架主要的接口形式之一。Flink也提供了关系型编程接口Table API以及基于Table API的SQL API,让用户能够通过使用结构化编程接口高效地构建Flink应用。同时Table API以及SQL能够统一处理批 量和实时计算业务,无须切换修改任何应用代码就能够基于同-套AP|编写流式应用和批量应用,从而达到真正意义的批流统一。
在Table API和SQL中,Flink可 以通过Table connector直接连接外部系统,将批量或者流式数据从外部系统中获取到Flink系统中,或者从Flink系统中将数据发送到外部系统中。其中对于数据接入,Table API中已经提供了TableSource从
外部系统获取数据,例如常见的数据库、文件系统等外部系统,对应的有OrcTableSource、HBase TableSource、CSVTableSource等常用的TableSource。对于数据输出,Table APl提供了T ableSink将Flink Table数据写入外部系统中,同时支持定义不同的文件格式类型,例如CsvTableSink、JDBCAppendTableSink和CassandraAppendTableSink等。
在Flink中通过Temporal Tables来表示其实数据元素一直不断变化的历史表,数据会随着时间的变化而发生变化。Temporal Tables底层其实维系了一张Append Only Table, Flink对数据表的变化进行Track,在查询操作中返回与指定时间点对应的版本的结果。没有临时表时,如果想关联查询某些变化的指标数据,就需要在关联的数据集中通过时间信息将最新的结果筛选出来,显然这种做法需要浪费大量的计算资源。但如果使用临时表,则可直接关联查询临时表,数据会通过不断地更新以保证查询的结果是最新的。Temporal Tables的目的就是简化用户查询语句,加速查询的速度,同时尽可能地降低对状态的使用,因为不需要维护大量的历史数据。
SQL作为Flink中提供的接口之一,占据着非常重要的地位,主要是因为SQL具有灵活和丰富的语法,能够应用于大部分的计算场景。Flink SQL底层使用Apache Calcite框架,将标准的Flink SQL语句解析并转换成底层的算子处理逻
辑,并在转换过程中基于语法规则层面进行性能优化,比如谓词下推等。另外用户在使用SQL编写Flink应用时,能够屏蔽底层技术细节,能够更加方便且高效地通过SQL语句来构建Flink应用。Flink SQL构建在Table API之上,并含盖了大部分的Table API功能特性。同时Flink SQL可以和Table API混用,Flink最终 会在整体上将代码合并在同一套代码逻辑中,另外构建一套SQL 代码可以同时应用在相同数据结构的流式计算场景和批量计算场景上,不需要用户对SQL语句做任何调整,最终达到实现批流统一的目的。
八、Flink组件栈
复杂事件处理(CEP) 是一种基于流处理的技术,将系统数据看作不同类型的事件,通过分析事件之间的关系,建立不同的事件关系序列库,并利用过滤、关联、聚合等技术,最终由简单事件产生高级事件,并通过模式规则的方式对重要信息进行跟踪和分析,从实时数据中发掘有价值的信息。复杂事件处理主要应用于防范网络欺诈、设备故障检测、风险规避和智能营销等领域。目前主流的CEP工具有Esper、Jboss Drools和商业版的MicroSoft Streamlnsight等,Flink基于DataStrem API提供了FlinkCEP组件栈,专门用于对复杂事件的处理,帮助用户从流式数据中发掘有价值的信息。
(1) 事件定义
简单事件:简单事件存在于现实场景中,主要的特点为处理单一事件,事件的定义可以直接观察出来,处理过程中无须关注多个事件之间的关系,能够通过简单的数据处理手段将结果计算出来。例如通过对当天的订单总额按照用户维度
进行汇总统计,超过一定数量之后进行报告。这种情况只需要计算每个用户每天的订单金额累加值,达到条件进行输出即可。复杂事件:相对于简单事件,复杂事件处理的不仅是单一的事件,也处理由多个事件组成的复合事件。复杂事件处理监测分析事件流(EventStreaming),当特定事件发生时来触发某些动作。
(2)事件关系
复杂事件中事件与事件之间包含多种类型关系,常见的有时序关系、聚合关系、层次关系、依赖关系及因果关系等。
时序关系: 动作事件和动作事件之间,动作事件和状态变化事件之间,都存在时间顺序。事件和事件的时序关系决定了大部分的时序规则,例如A事件状态持续为1的同时B事件状态变为0等。
聚合关系: 动作事件和动作事件之间,状态事件和状态事件之间都存在聚合关系,即个体聚合形成整体集合。例如A事件状态为1的次数为10触发预警。
层次关系: 动作事件和动作事件之间,状态事件和状态事件之间都存在层次关系,即父类事件和子类事件的层次关系,从父类到子类是具体化的,从子类到父类是泛化的。
依赖关系: 事物的状态属性之间彼此的依赖关系和约束关系。例如A事件状态触发的条件前提是B事件触发,则A与B事件之间就形成了依赖关系。
因果关系: 对于完整的动作过程,结果状态为果,初始状态和动作都可以视为原因。例如A事件状态的改变导致了B事件的触发,则A事件就是因,而B事件就是果。
(3)事件处理
复杂事件处理的目的是通过相应的规则对实时数据执行相应的处理策略,这些策略包括了推断、查因、决策、预测等方面的应用。
事件推断: 主要利用事物状态之间的约束关系,从一部分状态属性值可以推断出另一部分的状态属性值。例如由三角形一个角为90度及另一个角为45度,可以推断出第三个角为45度。
事件查因: 当出现结果状态,并且知道初始状态,可以查明某个动作是原因;同样当出现结果状态,并且知道之前发生了什么动作,可以查明初始状态是原因。当然反向的推断要求原因对结果来说必须是必要条件。
事件决策: 想得到某个结果状态,知道初始状态,决定执行什么动作。该过程和规则引擎相似,例如某个规则符合条件后触发行动,然后执行报警等操作。
事件预测: 该种情况知道事件初始状态,以及将要做的动作,预测未发生的结果状态。例如气象局根据气象相关的数据预测未来的天气情况等。
Pattern模式API
FlinkCEP中提供了Pattern API用于对输入流数据的复杂事件规则定义,并从事件流中抽取事件结果。通过使用Pattern API构建CEP应用程序,其中包括输入事件流的创建,以及Patter接口的定义, 然后通过CEP pattern方法将定义的Pattern应用在输入的Stream.上,最后使用PatternStream.select方法获取触发事件结果。以下实例是将温度大于35度的信号事件抽取出来,并产生事件报警,最后将结果输出到外部数据集中。
Flink Gelly组件库
早在2010年,Google就推出了著名的分布式图计算框架Pregel,之后Apache Spark社区也推出GraphX等图计算组件库,以帮助用户有效满足图计算领域的需求。Flink则通过封装DataSet API,形成图计算弓|擎Flink Gelly。同时
Gelly中的Graph API,基本涵盖了从图创建,图转换到图校验等多个方面的图操作接口,让用户能够更加简便高效地开发图计算应用。
Graph API则是Gelly构建在DataSet API之.上的更高级API,主要功能是降低使用Flink编写图计算应用的成本。在Graph API中提供了非常丰富的图操作接口,例如对图的生成、转换以及修改等。
在使用创建好的Graph之前,一般需 要对Graph的正确性进行检验,从而确保Graph可用。在Gelly中提供了validate()方法可以对Graph进行校验,方法的参数是GraphValidator接口实现类,通过实现GraphValidator中的validate()方法定义
对Graph进行逻辑检验。同时Flink提供了默认的GraphValidator实现类InvalidVertexldsValidator,能够简单检测Edges中每条边的顶点ID是否全部存在于Vertex集合中,即所有Edge的ID必须都存在于Vertices IDs集合中。
在图计算的过程中伴随着非常多的大规模迭代计算场景,例如求取有向图的最短路径等问题。而在Gelly中针对迭代图分别提供了Vertex- -Centric Iterations、Scatter- Gather lterations、Gather- Sum- Apply Iterations三种迭代计算方式,每种迭代方式中均使用到了不同的策略及优化手段,极大地增强了迭代类图计算应用的效率。
Vertex-Centric迭代是以顶点为中心进行迭代,在计算过程中会以顶点为中心分别计算每个顶点上的边或邻近顶点等指标。其中单次迭代的过程被称为Supersteps,在每次迭代中都会并行执行已经定义的Function,同顶点和顶点之间通过Message进行通信,一个顶点可以向另外-一个顶点发送信息,前提是这个顶点知道其他顶点的ID。
Scatter-Gather迭代也被称为“signal/collect"迭代,共包含了Scatter和Gather两个阶段,Scatter阶段主要 是将一个顶点上的Message发送给其他顶点,Gather阶段根据接收到的Message更新顶点的Value。和Vertex-Centric迭代相
似,Scatter-Gather迭代对图中的顶点进行迭代计算,每次迭代计算的过程也被称为supersteps,在supersteps中计算每个顶点为其他顶点生成Message,同时让接收到其他节点的Message更新自己的Value。
与Scatter-Gather迭代不同的是,Gather-Sum-Apply迭代中的superstep共包含了三个阶段,分别为Gather、 Sum和Apply阶段, 其中Gater阶段并行地在每个顶点上执行自定义GatherFunction,计算边或邻近顶点的指标,形成部分结果值。然后进入到Sum阶段,在Sum阶段将Gather阶段生成的部分结果进行合并,生成单一指标, 这步是通过自定义Reducer函数实现。最后Apply阶段,根据Sum阶段生成的结果,判断并更新Vertex.上的指标。在整个迭代过程中,三个阶段需要同步在一个superstep中进行,当全部完成才能进入到下一个superstep。同时执行完最大的迭代次数后,完成整个迭代任务,返回新生成的Graph。
FlinkML机器学习应用
机器学习是一门多领域交叉学科,涵盖设计概率论、统计学、逼近轮等多门学科。机器学习主要是用来设计和分析一些可以让机器自动学习的算法,这些算法是从数据中自动分析获取规律,并利用已经学习的规律对未知数据进行预测,
产生预测结果。而机器学习共分为几大种类型,分别是监督学习、半监督学习、无监督学习以及强化学习,每种学习类型具有相应的算法集。例如对于无监督学习来说,对应的是聚类算法,有监督学习则对应的是分类与回归算法等。
和其他分布式处理框架一样,Flink基 于DataSet API之.上,封装出针对机器学习领域的组件栈FlinkML。在FlinkML 中提供了诸如分类、聚类、推荐等常用的算法,用户可以直接使用这些算法构建相应的机器学习应用。虽然目前FlinkML中集成的算法相对较少,但是Flink社区 会在未来的版本中陆续集成更多的算法。
和SparkML ib-样,FlinkML 也是通过使用Breeze库实现了底层的向量和矩阵等数据结构,用以辅助构建算法模型,其中Breeze库提供了Vector/Matrix的实现以及对应的计算接口。在FlinkML 中则使用了自己定义的Vector和Matrix,只是在具体的计算过程中通过转换为Breeze的形式进行运算。在FlinkML中可以通过两种方式来构建Vector数据,分别是读取L ibSVM数据或读取文件数据转换成DataSet[String]数据集,然后再通过Map算子将数据集转换为DataSet[L abelVector]数据集。
(1)通过读取L ibSVM数据
FlinkML中提供了ML Utils类的readLibSVM方法,用于读取L IBSVM格式类型的数据,同时提供writeL ibSVM方法将Flink中的数据以LIBSVM格式输出到外部文件系统中。
(2)读取CSV文件转换
可以通过DataSet API中提供读取文件数据的方式,将外部数据读取到Flink系统中,并通过Map函数将数据集转换成LabelVector类型。
有监督学习算子
1. SVM算法
在机器学习中,支持向量机(SVM) 是在分类与回归分析中分析数据的监督式学习模型。SVM模型是将实例表示为空间中的点,通过映射使得单独类别的实例被尽可能宽的明显间隔分开,然后将新的实例映射到同一空间中,并基于它们落在间隔的哪一侧来预测所属类别。除了进行线性分类外,SVM还可以使用所谓的核技巧有效地进行非线性分类,并将其输入隐式映射到高维特征空间中。在Flink中SVM是一个预测函数,含有ft和predict两个方法, 其中ft方法是基于训练数据集进行转化和训练,生成SVM模型;而Predict方法使用已经训练好的模型进行预测,并生成LabelVector类型预测结果。
2. 多元线性回归
多元线性回归模型主要用回归方程描述一个因变量与多个自变量的依存关系。和其他预测算法一样,在Flink ML中多元线性回归算法中包含fht和predict两个方法,分别对用户训练模型和基于模型进行预测,其中ft方法传入的是LablePoint数据结构的dataset数据集,并返回模型结果。predict方法中可以有所有Vector接口的实现类,方法结果返回的是含有输入参数和Double类型的打分结果。
数据预处理
在FlinkML中实现了基本的数据预处理方法,其中包括多项式特征、标准化、区间化等常用方法,这些算子都继承于Transformer接口,并使用fht方法从训练集学习模型(例如,归一化的平均值和标准偏差)。
1. 多项式特征
特征加工的过程中,通过增加一些输入数据的非线性特征来增加模型的复杂度通常是非常有效的,可以获得特征的更高维度和相互间的关系项。当多项式特征比较少的时候,可以对很少的特征进行多项式变化,产生更多的特征。多项式变换就是把现有的特征排列组合相乘,例如如果是degree为2的变换,则会把现有的特征中,抽取两个相乘,并获得所有组合的结果。
2. 标准化
标准化处理函数的主要目的是根据用户统计出来的均值和方差对数据集进行标准化处理,防止因为度量单位不同而导致计算出现的偏差。标准化处理函数通过使用均值来对某个特征进行中心化,然后除以非常量特征(non- constant features)的标准差进行缩放。
3.区间缩放
区间缩放是将某-列向量根据最大值和最小值进行区间缩放,将指标转换到指定范围的区间内,从而尽可能地使特征的度量标准保持一致,避免因为某些指标比较大的特征在训练模型过程中占有太大的权重,影响整个模型的效果。
机器学习已经被成功应用到多个领域,如智能推荐、自然语言处理、模式识别等。但是不管是什么类型的机器学习应用,其实都基本遵循着相似的流程,包括数据源接入、数据预处理、特征抽取、模型训练、模型预估、模型可视化等步骤。如果能够将这些步骤有效连接并形成流水线式的数据处理模式,将极大地提升机器学习应用构建的效率。受Scikit-Learn开源项目的启发,在FlinkML库中提供了Pipelines的设计,将数据处理和模型预测算子进行连接,以提升整体机器学习应用的构建效率。同时,FlinkML中的算法基本上都实现于Transformers和Predictors接口,主要目的就是为了能够提供一整套的ML Pipelines, 帮助用户构建复杂且高效的机器学习应用。
九、Flink集群部署
作为通用的分布式数据处理框架,Flink可 以基于Standalone集群进行分布式计算,也可以借助于第三方组件提供的计算资源完成分布式计算。目前比较熟知的资源管理器有Hadoop Yarn、Apache Mesos、Kubernetes等,目前Flink已经能够非常良好地支持这些资源管理器。
Flink是Master- Slave架构的分布式处理框架,因此构建Standalone Cluster需要同时配置这两种服务角色,其中Master角 色对应的是JobManager, Slave角 色对应的是TaskManager。
Hadoop Yarn是Hadoop 2.x提出的统一资 源管理器,也是目前相对比较流行的大数据资源管理平台,Spark和MapReduce等 分布式处理框架都可以兼容并运行在Yarn.上,Flink也是如此。需要注意Flink应用提交到Yarn上目前支持两种模式,- 种是Yarn Session Model,这种模式中Flink会向Hadoop Yarn申请足够多的资源,并在Yarn上启动长时间运行的Flink Session集群,用户可以通过RestAPI或Web页面将Flink任务提交到Flink Session集群上运行。另外-种为Single Job Model和大多数计算框架的使用方式类似,每个Flink任务 单独向Yarn提交一个Application, 并且每个任务都有自己的JobManager和TaskManager,当任务结束后对应的组件也会随任务释放。
容器化部署是目前业界非常流行的一项技术,基于Docker镜像运行能够让用户更加方便地对应用进行管理和运维。随着Docker容 器编排工具Kubernetes近几年逐渐流行起来,大多数企业也逐渐使用Kubernetes来管理集群容器资源。Flink也在最近的版本中支持了Kubernetes部署模式,让用户能够基于Kubernetes来构建Flink Session Cluster,也可以通过Docker镜像的方式向Kuernetes集群中提交,独立的Flink任务。
目前Flink高可用配置仅支持Standalone Cluster和Yarn Cluster两种集群模式,同时Flink高可用配置中主要针对JobManager的高可用保证,因为JobManager是整个集群的管理节点,负责整个集群的任务调度和资源管理,如果JobManager出现问题将会导致新Job无法提交,并且已经执行的Job也将全部失败,因此对JobManager的高可用保证尤为重要。
Standalone集群中的JobManager高可用主要借助Zookeeper来完成,Zookeeper作为大数据生态中的分布式协调管理者,主要功能是为分布式系统提供一致性协调(Coordination) 服务。在Flink Standalone集群中,JobManager的服务信息会被注册到Zookeeper中,并通过Zookeeper完成JobManager Leader的选举。Standalone集群 会同时存在多个JobManager,但只有一个处于工作状态,其他处于Standby状态 ,当Active JobManager失去连接后(如系统宕机),Zookeeper会 自动从Standby中选举新的JobManager来接管Flink集群。
在Flink Yarn Session集群模式下,高可用主要依赖于Yarn协助进行,主要因为Yarn本身对运行在Yarn.上的应用具有一定的容错保证。前面已经了解到Flink On Yarn的模式其实是将Flink JobManager执行在ApplicationMaster所在的容器之中,同时Yarn不会启动多个JobManager达到高可用目的,而是通过重启的方式保证JobManager高可用。
作为分布式计算框架,Flink需 要访问外部数据以及获取计算资源才能完成具体的计算任务。而在开启安全认证的集群中,一般是通过Kerberos服务对用户和服务进行双向认证,Kerberos也是目前大数据生态中比较通用的安全认证协议,Flink也只支持通过Kerberos进行网络安全认证。
Flink的任务在停止或启动过程中,可以通过使用savepoint命令将Flink整个任务状态持久化到磁盘中。如以命令对正在执行的Flink任务进行savepoint操作,首先通过指定jobID确定需要重启的应用,然后通过指定Savepoint存储路径, 将算子状态数据持久化到指定的本地路径中。
十、Flink监控与性能优化
Flink任务提交到集群后,接下来就是对任务进行有效的监控。Flink将任务监控指标主要分为系统指标和用户指标两种:系统指标主要包括Flink集群层面的指标,例如CPU负载,各组件内存使用情况等;用户指标主要包括用户在任务中自定义注册的监控指标,用于获取用户的业务状况等信息。Flink中的监控指标可以通过多种方式获取,例如可以从Flink UI中直接查看,也可以通过Rest Api或Reporter获取。
Flink提供了非常丰富的监控指标Reporter,可以将采集到的监控指标推送到外部系统中。通过在conf/fink- conf.yaml中配 置外部系统的Reporter,在Flink集群启动的过程中就会将Reporter配置加载到集群环境中,然后就可以把Flink系统中的监控指标输出到Reporter对应的外部监控系统中。目前Flink支持的Reporter有JMX、Graphite、 Prometheus、 StatsD、 Datadog、SIf4j等 系统,且每种系统对应的Reporter均已在Flink中实现,用户可以直接配置使用。
反压在流式系统中是一种非常重要的机制,主要作用是当系统中下游算子的处理速度下降,导致数据处理速率低于数据接入的速率时,通过反向背压的方式让数据接入的速率下降,从而避免大量数据积压在Flink系统中,最后系统无法正常运行。Flink具有 天然的反压机制,不需要通过额外的配置就能够完成反压处理。
通过触发JVM进程采样的方式获取到反压监控数据,同时Flink会将 反压状态分为三个级别,分别为OK、LOW、HIGH级别,其中OK对应的反压比例为大于0小于10%,LOW对应的反压比例大于10%小于50%,HIGH对应的反压比例大于50%小于100%。