文章目录
一、前言
二、什么是Apache Flink
2.1 Flink Application
2.2 Flink Architecture
2.3 Flink 重要特点
2.3.1 事件驱动型(Event-driven)
2.3.2 流与批的世界观
2.3.3 分层API
2.4 Flink 应用场景
2.4.1 Flink 应用场景:Data Pipeline
2.4.2 Flink 应用场景:Data Analytics
2.4.3 Flink 应用场景:Data Driven
2.5 Flink 优势
2.5.1 状态容错
2.5.1.1 简单场景的精确一次容错方法
2.5.1.2 分布式状态容错
2.5.1.3 分散式快照(Distributed Snapshots)方法
2.5.2 状态维护(状态后端)
2.5.3 Event – Time
2.5.3.1 不同时间种类
2.5.3.2 Event – Time 处理
2.5.3.3 Watermarks
2.5.4 状态保存与迁移
三、什么是阿里云实时计算
3.1 产品特点
3.2 产品定位
3.3 基本概念
3.4 产品形态
3.5 应用场景
3.5.1 已有流处理系统迁移
3.5.2 按照部门场景划分
3.5.3 按照技术领域划分
3.5.4 场景实践
3.6 使用限制
3.6.1 支持地域
3.6.2 CU处理能力
3.6.3 作业、任务数量限制
四、Flink vs. Blink
4.1 Spark Streaming、Kafka Streams、Storm等存在的问题
4.2 Flink的优势
4.3 Flink和Blink的主要区别
4.4 流数据的SQL查询存在的难点,以及Blink的解决方案
五、Referece
一、前言
Apache Flink 是一个分布式大数据处理引擎,可对有限数据流和无限数据流进行有状态计算。可部署在各种集群环境,对各种大小的数据规模进行快速计算。
阿里云实时计算(Alibaba Cloud Realtime Compute)则是一套基于Apache Flink构建的一站式、高性能实时大数据处理平台,广泛适用于流式数据处理、离线数据处理等场景。
在这里我们对实时计算Flink和Alibaba Cloud Realtime Compute相关的知识点(能力、限制、典型场景,区别)进行分析。
二、什么是Apache Flink
Flink 项目的理念是:“Apache Flink 是为分布式、高性能、随时可用以及准确的流处理应用程序打造的开源流处理框架”。Apache Flink 是一个框架和分布式处理引擎,用于对无界和有界数据流进行有状态计算。Flink 被设计在所有常见的集群环境中运行,以内存执行速度和任意规模来执行计算。
我们可以先从Apache Flink的定义、架构、特点和优势等来进行了解。
2.1 Flink Application
首先,我觉得Flink 应用开发需要先理解 Flink 的Streams、State、Time 等基础处理语义以及Flink 兼顾灵活性和方便性的多层次API。
- Streams:流,分为有限数据流与无限数据流,unbounded stream 是有始无终的数据流,即无限数据流;而bounded stream 是限定大小的有始有终的数据集合,即有限数据流,二者的区别在于无限数据流的数据会随时间的推演而持续增加,计算持续进行且不存在结束的状态,相对的有限数据流数据大小固定,计算最终会完成并处于结束的状态。
- State,状态是计算过程中的数据信息,在容错恢复和 Checkpoint 中有重要的作用,流计算在本质上是Incremental Processing(增量处理),因此需要不断查询保持状态;另外,为了确保Exactly- once 语义,需要数据能够写入到状态中;而持久化存储,能够保证在整个分布式系统运行失败或者挂掉的情况下做到Exactly- once,这是状态的另外一个价值。
- Time,分为Event time、Ingestion time、Processing time,Flink 的无限数据流是一个持续的过程,时间是我们判断业务状态是否滞后,数据处理是否及时的重要依据。
- API,API 通常分为三层,由上而下可分为SQL / Table API、DataStream API、ProcessFunction 三层,API 的表达能力及业务抽象能力都非常强大,但越接近SQL 层,表达能力会逐步减弱,抽象能力会增强,反之,ProcessFunction 层API 的表达能力非常强,可以进行多种灵活方便的操作,但抽象能力也相对越小。
2.2 Flink Architecture
在架构部分,主要分为以下四点:
- 第一,Flink 具备统一的框架处理有界和无界两种数据流的能力
- 第二, 部署灵活,Flink 底层支持多种资源调度器,包括Yarn、Kubernetes 等。Flink 自身带的Standalone 的调度器,在部署上也十分灵活。
- 第三, 极高的可伸缩性,可伸缩性对于分布式系统十分重要,阿里巴巴双11大屏采用Flink 处理海量数据,使用过程中测得Flink 峰值可达17 亿/秒。
- 第四, 极致的流式处理性能。Flink 相对于Storm 最大的特点是将状态语义完全抽象到框架中,支持本地状态读取,避免了大量网络IO,可以极大提升状态存取的性能。
2.3 Flink 重要特点
2.3.1 事件驱动型(Event-driven)
事件驱动型应用是一类具有状态的应用,它从一个或多个事件流提取数据,并根据到来的事件触发计算、状态更新或其他外部动作。比较典型的就是以 kafka 为代表的消息队列几乎都是事件驱动型应用。
与之不同的就是 Spark Streaming 微批次,如图:
事件驱动型:
2.3.2 流与批的世界观
批处理的特点是有界、持久、大量,非常适合需要访问全套记录才能完成的计算工作,一般用于离线统计。
流处理的特点是无界、实时, 无需针对整个数据集执行操作,而是对通过系统传输的每个数据项执行操作,一般用于实时统计。
在 Spark 的世界观中,一切都是由批次组成的,离线数据是一个大批次,而实时数据是由一个一个无限的小批次组成的。
而在 Flink 的世界观中,一切都是由流组成的,离线数据是有界限的流,实时数据是一个没有界限的流,这就是所谓的有界流和无界流。
无界数据流:无界数据流有一个开始但是没有结束,它们不会在生成时终止并提供数据,必须连续处理无界流,也就是说必须在获取后立即处理 event。对于无界数据流我们无法等待所有数据都到达,因为输入是无界的,并且在任何时间点都不会完成。处理无界数据通常要求以特定顺序(例如事件发生的顺序)获取 event,以便能够推断结果完整性。
有界数据流:有界数据流有明确定义的开始和结束,可以在执行任何计算之前通过获取所有数据来处理有界流,处理有界流不需要有序获取,因为可以始终对有界数据集进行排序,有界流的处理也称为批处理。
这种以流为世界观的架构,获得的最大好处就是具有极低的延迟。
2.3.3 分层API
最底层级的抽象仅仅提供了有状态流,它将通过过程函数(Process Function)被嵌入到 DataStream API 中。底层过程函数(Process Function) 与 DataStream API 相集成,使其可以对某些特定的操作进行底层的抽象,它允许用户可以自由地处理来自一个或多个数据流的事件,并使用一致的容错的状态。除此之外,用户可以注册事件时间并处理时间回调,从而使程序可以处理复杂的计算。
实际上,大多数应用并不需要上述的底层抽象,而是针对核心 API(Core APIs) 进行编程,比如 DataStream API(有界或无界流数据)以及 DataSet API(有界数据集)。这些 API 为数据处理提供了通用的构建模块,比如由用户定义的多种形式的转换(transformations),连接(joins),聚合(aggregations),窗口操作(windows)等等。DataSet API 为有界数据集提供了额外的支持,例如循环与迭代。这些 API处理的数据类型以类(classes)的形式由各自的编程语言所表示。
Table API 是以表为中心的声明式编程,其中表可能会动态变化(在表达流数据时)。
Table API 遵循(扩展的)关系模型:表有二维数据结构(schema)(类似于关系数据库中的表),同时 API 提供可比较的操作,例如 select、project、join、group-by、 aggregate 等。Table API 程序声明式地定义了什么逻辑操作应该执行,而不是准确地确定这些操作代码的看上去如何。
尽管 Table API 可以通过多种类型的用户自定义函数(UDF)进行扩展,其仍不如核心 API 更具表达能力,但是使用起来却更加简洁(代码量更少)。除此之外, Table API 程序在执行之前会经过内置优化器进行优化。
你可以在表与 DataStream/DataSet 之间无缝切换,以允许程序将 Table API 与 DataStream 以及 DataSet 混合使用。
Flink 提供的最高层级的抽象是 SQL 。这一层抽象在语法与表达能力上与 Table API 类似,但是是以 SQL 查询表达式的形式表现程序。SQL 抽象与 Table API交互密切,同时 SQL 查询可以直接在 Table API 定义的表上执行。
目前 Flink 作为批处理还不是主流,不如 Spark 成熟,所以 DataSet 使用的并不是很多。Flink Table API 和 Flink SQL 也并不完善,大多都由各大厂商自己定制。实际上 Flink 作为最接近 Google DataFlow模型的实现,是流批统一的观点,所以基本上使用 DataStream 就可以了。
2.4 Flink 应用场景
我有在社区看了很多师兄分享了他们在自己公司里面基于Flink做的一些实践,包括携程、唯品会、饿了么、滴滴、头条等等。他们的应用场景大多包括实时的机器学习,实时的统计分析,实时的异常监测等等,这些实践案例的共同点就是都用来做实时性的任务。
2.4.1 Flink 应用场景:Data Pipeline
Data Pipeline 的核心场景类似于数据搬运并在搬运的过程中进行部分数据清洗或者处理,而整个业务架构图的左边是Periodic ETL,它提供了流式ETL 或者实时ETL,能够订阅消息队列的消息并进行处理,清洗完成后实时写入到下游的Database或File system 中。场景举例:
- 实时数仓
当下游要构建实时数仓时,上游则可能需要实时的Stream ETL。这个过程会进行实时清洗或扩展数据,清洗完成后写入到下游的实时数仓的整个链路中,可保证数据查询的时效性,形成实时数据采集、实时数据处理以及下游的实时Query。
- 搜索引擎推荐
搜索引擎这块以淘宝为例,当卖家上线新商品时,后台会实时产生消息流,该消息流经过Flink 系统时会进行数据的处理、扩展。然后将处理及扩展后的数据生成实时索引,写入到搜索引擎中。这样当淘宝卖家上线新商品时,能在秒级或者分钟级实现搜索引擎的搜索。
2.4.2 Flink 应用场景:Data Analytics
Data Analytics,如图,左边是Batch Analytics,右边是Streaming Analytics。Batch Analytics 就是传统意义上使用类似于Map Reduce、Hive、Spark Batch 等,对作业进行分析、处理、生成离线报表;Streaming Analytics 使用流式分析引擎如Storm、Flink 实时处理分析数据,应用较多的场景如实时大屏、实时报表。
- 移动应用中的用户行为分析
- 消费者技术中的实时数据即席查询
2.4.3 Flink 应用场景:Data Driven
从某种程度上来说,所有的实时的数据处理或者是流式数据处理都是属于Data Driven,流计算本质上是Data Driven 计算。应用较多的如风控系统,当风控系统需要处理各种各样复杂的规则时,Data Driven 就会把处理的规则和逻辑写入到Datastream 的API 或者是ProcessFunction 的API 中,然后将逻辑抽象到整个Flink 引擎,当外面的数据流或者是事件进入就会触发相应的规则,这就是Data Driven 的原理。在触发某些规则后,Data Driven 会进行处理或者是进行预警,这些预警会发到下游产生业务通知,这是Data Driven 的应用场景,Data Driven 在应用上更多应用于复杂事件的处理。
- 实时推荐(例如在客户浏览商家页面的同时进行商品推荐)
- 模式识别或复杂事件处理(例如根据信用卡交易记录进行欺诈识别)
- 异常检测(例如计算机网络入侵检测)
2.5 Flink 优势
Flink 对于有状态流式处理的挑战,主要有以下几点:状态容错、状态维护、Event-time 处理、状态保存与迁移。
2.5.1 状态容错
当我们考虑状态容错时难免会想到精确一次的状态容错,应用在运算时累积的状态,每笔输入的事件反映到状态,更改状态都是精确一次,如果修改超过一次的话也意味着数据引擎产生的结果是不可靠的。
- 如何确保状态拥有精确一次(Exactly-once guarantee)的容错保证?
- 如何在分散式场景下替多个拥有本地状态的运算子产生一个全域一致的快照(Global consistent -snapshot)?
- 更重要的是,如何在不中断运算的前提下产生快照?
2.5.1.1 简单场景的精确一次容错方法
还是以使用者出现次数来看,如果某个使用者出现的次数计算不准确,不是精确一次,那么产生的结果是无法作为参考的。在考虑精确的容错保证前,我们先考虑最简单的使用场景,如无限流的数据进入,后面单一的Process 进行运算,每处理完一笔计算即会累积一次状态,这种情况下如果要确保Process 产生精确一次的状态容错,每处理完一笔数据,更改完状态后进行一次快照,快照包含在队列中并与相应的状态进行对比,完成一致的快照,就能确保精确一次。
2.5.1.2 分布式状态容错
Flink作为分布式的处理引擎,在分布式的场景下,进行多个本地状态的运算,只产生一个全域一致的快照,如需要在不中断运算值的前提下产生全域一致的快照,就涉及到分散式状态容错。
关于Global consistent snapshot,当Operator 在分布式的环境中,在各个节点做运算,首先产生Global consistent snapshot 的方式就是处理每一笔数据的快照点是连续的,这笔运算流过所有的运算值,更改完所有的运算值后,能够看到每一个运算值的状态与该笔运算的位置,即可称为Consistent snapshot,当然,Global consistent snapshot 也是简易场景的延伸。
容错恢复
首先了解一下Checkpoint,上面提到连续性快照每个Operator 运算值本地的状态后端都要维护状态,也就是每次将产生检查点时会将它们传入共享的DFS 中。当任何一个Process 挂掉后,可以直接从三个完整的Checkpoint 将所有的运算值的状态恢复,重新设定到相应位置。Checkpoint的存在使整个Process 能够实现分散式环境中的Exactly-once。
2.5.1.3 分散式快照(Distributed Snapshots)方法
关于Flink 如何在不中断运算的状况下持续产生Global consistent snapshot,其方式是基于用 Simple lamport 演算法机制下延伸的。已知的一个点Checkpoint barrier,Flink 在某个Datastream 中会一直安插Checkpoint barrier,Checkpoint barrier 也会N – 1等等,Checkpoint barrier N 代表着所有在这个范围里面的数据都是Checkpoint barrier N。
举例:假设现在需要产生Checkpoint barrier N,但实际上在Flink 中是由Job manager 触发Checkpoint,Checkpoint 被触发后开始从数据源产生Checkpoint barrier。当Job 开始做Checkpoint barrier N 的时候,可以理解为Checkpoint barrier N 需要逐步填充左下角的表格。
如图,当部分事件标为红色,Checkpoint barrier N 也是红色时,代表着这些数据或事件都由Checkpoint barrier N 负责。Checkpoint barrier N 后面白色部分的数据或事件则不属于Checkpoint barrier N。
在以上的基础上,当数据源收到Checkpoint barrier N 之后会先将自己的状态保存,以读取Kafka资料为例,数据源的状态就是目前它在Kafka 分区的位置,这个状态也会写入到上面提到的表格中。下游的Operator 1 会开始运算属于Checkpoint barrier N 的数据,当Checkpoint barrier N 跟着这些数据流动到Operator 1 之后,Operator 1 也将属于Checkpoint barrier N 的所有数据都反映在状态中,当收到Checkpoint barrier N 时也会直接对Checkpoint去做快照。
当快照完成后继续往下游走,Operator 2 也会接收到所有数据,然后搜索Checkpoint barrier N 的数据并直接反映到状态,当状态收到Checkpoint barrier N 之后也会直接写入到Checkpoint N 中。以上过程到此可以看到Checkpoint barrier N 已经完成了一个完整的表格,这个表格叫做Distributed Snapshots,即分布式快照。分布式快照可以用来做状态容错,任何一个节点挂掉的时候可以在之前的Checkpoint 中将其恢复。继续以上Process,当多个Checkpoint 同时进行,Checkpoint barrier N 已经流到Job manager 2,Flink job manager 可以触发其他的Checkpoint,比如Checkpoint N + 1,Checkpoint N + 2 等等也同步进行,利用这种机制,可以在不阻挡运算的状况下持续地产生Checkpoint。
2.5.2 状态维护(状态后端)
状态维护即用一段代码在本地维护状态值,当状态值非常大时需要本地的状态后端来支持。
如图,在Flink 程序中,可以采用getRuntimeContext().getState(desc); 这组API 去注册状态。Flink 有多种状态后端,采用API 注册状态后,读取状态时都是通过状态后端来读取的。Flink 有两种不同的状态值,也有两种不同的状态后端:
- JVM Heap状态后端,适合数量较小的状态,当状态量不大时就可以采用JVM Heap 的状态后端。JVM Heap 状态后端会在每一次运算值需要读取状态时,用Java object read / writes 进行读或写,不会产生较大代价,但当Checkpoint 需要将每一个运算值的本地状态放入Distributed Snapshots 的时候,就需要进行序列化了。
- RocksDB状态后端,它是一种out of core 的状态后端。在Runtime 的本地状态后端让使用者去读取状态的时候会经过磁盘,相当于将状态维护在磁盘里,与之对应的代价可能就是每次读取状态时,都需要经过序列化和反序列化的过程。当需要进行快照时只将应用序列化即可,序列化后的数据直接传输到中央的共享DFS 中。
Flink目前支持以上两种状态后端,一种是纯 Memory 的状态后端,另一种是有资源磁盘的状态后端,在维护状态时可以根据状态的数量选择相应的状态后端。
2.5.3 Event – Time
2.5.3.1 不同时间种类
在Flink 及其他进阶的流式处理引擎出现之前,大数据处理引擎一直只支持Processing-time 的处理。假设定义一个运算 Windows 的窗口,Windows 运算设定每小时进行结算。Processing-time 进行运算时,可以发现数据引擎将3 点至4 点间收到的数据进行结算。实际上在做报表或者分析结果时是想了解真实世界中3 点至4 点之间实际产生数据的输出结果,了解实际数据的输出结果就必须采用Event – Time 了。
如图,Event – Time 相当于事件,它在数据最源头产生时带有时间戳,后面都需要用时间戳来进行运算。用图来表示,最开始的队列收到数据,每小时对数据划分一个批次,这就是Event – Time Process 在做的事情。
2.5.3.2 Event – Time 处理
Event – Time 是用事件真实产生的时间戳去做Re-bucketing,把对应时间3 点到4 点的数据放在3 点到4 点的Bucket,然后Bucket 产生结果。所以Event – Time 跟Processing – time 的概念是这样对比的存在。
Event – Time 的重要性在于记录引擎输出运算结果的时间。简单来说,流式引擎连续24 小时在运行、搜集资料,假设Pipeline 里有一个 Windows Operator 正在做运算,每小时能产生结果,何时输出 Windows的运算值,这个时间点就是Event – Time 处理的精髓,用来表示该收的数据已经收到。
2.5.3.3 Watermarks
Flink实际上是用 Watermarks 来实现Event – Time 的功能。Watermarks 在Flink 中也属于特殊事件,其精髓在于当某个运算值收到带有时间戳“ T ”的 Watermarks 时就意味着它不会接收到新的数据了。使用Watermarks 的好处在于可以准确预估收到数据的截止时间。举例,假设预期收到数据时间与输出结果时间的时间差延迟5 分钟,那么Flink 中所有的 Windows Operator 搜索3 点至4 点的数据,但因为存在延迟需要再多等5分钟直至收集完4:05 分的数据,此时方能判定4 点钟的资料收集完成了,然后才会产出3 点至4 点的数据结果。这个时间段的结果对应的就是 Watermarks 的部分。
2.5.4 状态保存与迁移
流式处理应用无时无刻不在运行,运维上有几个重要考量:
- 更改应用逻辑/修bug 等,如何将前一执行的状态迁移到新的执行?
- 如何重新定义运行的平行化程度?
- 如何升级运算丛集的版本号?
Checkpoint完美符合以上需求,不过Flink 中还有另外一个名词保存点(Savepoint),当手动产生一个Checkpoint 的时候,就叫做一个Savepoint。Savepoint 跟Checkpoint 的差别在于Checkpoint是Flink 对于一个有状态应用在运行中利用分布式快照持续周期性的产生Checkpoint,而Savepoint 则是手动产生的Checkpoint,Savepoint 记录着流式应用中所有运算元的状态。
如图,Savepoint A 和Savepoint B,无论是变更底层代码逻辑、修bug 或是升级Flink 版本,重新定义应用、计算的平行化程度等,最先需要做的事情就是产生Savepoint。
Savepoint产生的原理是在Checkpoint barrier 流动到所有的Pipeline 中手动插入从而产生分布式快照,这些分布式快照点即Savepoint。Savepoint 可以放在任何位置保存,当完成变更时,可以直接从Savepoint 恢复、执行。
从Savepoint 的恢复执行需要注意,在变更应用的过程中时间在持续,如Kafka 在持续收集资料,当从Savepoint 恢复时,Savepoint 保存着Checkpoint 产生的时间以及Kafka 的相应位置,因此它需要恢复到最新的数据。无论是任何运算,Event – Time 都可以确保产生的结果完全一致。
假设恢复后的重新运算用Process Event – Time,将 Windows 窗口设为1 小时,重新运算能够在10 分钟内将所有的运算结果都包含到单一的 Windows 中。而如果使用Event – Time,则类似于做Bucketing。在Bucketing 的状况下,无论重新运算的数量多大,最终重新运算的时间以及Windows 产生的结果都一定能保证完全一致。