专栏原创出处:github-源笔记文件 github-源码 ,欢迎 Star,转载请附上原文出处链接和本声明。

# 1.基础概念

Flink 通过实现 Google DataFlow 流式计算模型实现了高吞吐、低延迟、高性能兼具实时流式计算框架。
同时 Flink 支持高度容错的状态管理,防止状态在计算过程中因为系统异常而出现丢失,Flink 周期性地通过分布式快照技术 Checkpoints 实现状态的持久化维护,使得即使在系统停机或者异常的情况下都能计算出正确的结果。

# 1.1 优势

1.同时支持高吞吐、低延迟、高性能
Flink 是目前开源社区中唯一一套集高吞吐、低延迟、高性能三者于一身的分布式流式数据处理框架。
像 Apache Spark 也只能兼顾高吞吐和高性能特性,主要因为在 Spark Streaming 流式计算中无法做到低延迟保障;
而流式计算框架 Apache Storm 只能支持低延迟和高性能特性,但是无法满足高吞吐的要求。
而满足高吞吐、低延迟、高性能这三个目标对分布式流式计算框架来说是非常重要的。

2.支持事件时间(Event Time)概念 在流式计算领域中,窗口计算的地位举足轻重,但目前大多数框架窗口计算采用的都是系统时间(Process Time),也是事件传输到计算框架处理时,系统主机的当前时间。
Flink 能够支持基于事件时间(Event Time)语义进行窗口计算,也就是使用事件产生的时间,这种基于事件驱动的机制使得事件即使乱序到达,流系统也能够计算出精确的结果,保持了事件原本产生时的时序性,尽可能避免网络传输或硬件系统的影响。

3.支持有状态计算 所谓状态就是在流式计算过程中将算子的中间结果数据保存在内存或者文件系统中,等下一个事件进入算子后可以从之前的状态中获取中间结果中计算当前的结果,从而无须每次都基于全部的原始数据来统计结果,
这种方式极大地提升了系统的性能,并降低了数据计算过程的资源消耗。
对于数据量大且运算逻辑非常复杂的流式计算场景,有状态计算发挥了非常重要的作用。
Flink 在 1.4 版本中实现了状态管理

4.支持高度灵活的窗口(windows)操作 在流处理应用中,数据是连续不断的,需要通过窗口的方式对流数据进行一定范围的聚合计算
例如统计在过去的 1 分钟内有多少用户点击某一网页,在这种情况下,我们必须定义一个窗口,用来收集最近一分钟内的数据,并对这个窗口内的数据进行再计算。
Flink 将窗口划分为基于 Time、Count、Session,以及 Data-driven 等类型的窗口操作,窗口可以用灵活的触发条件定制化来达到对复杂的流传输模式的支持,用户可以定义不同的窗口触发机制来满足不同的需求。

5.基于轻量级分布式快照(Snapshot)实现的容错 Flink 能够分布式运行在上千个节点上,将一个大型计算任务的流程拆解成小的计算过程,然后将 task 分布到并行节点上进行处理。
在任务执行过程中,能够自动发现事件处理过程中的错误而导致数据不一致的问题,比如:节点宕机、网路传输问题,或是由于用户因为升级或修复问题而导致计算服务重启等。
在这些情况下,通过基于分布式快照技术的 Checkpoints,将执行过程中的状态信息进行持久化存储,一旦任务出现异常停止,Flink 就能够从 Checkpoints 中进行任务的自动恢复,以确保数据在处理过程中的一致性。

6.基于 JVM 实现独立的内存管理 内存管理是所有计算框架需要重点考虑的部分,尤其对于计算量比较大的计算场景,数据在内存中该如何进行管理显得至关重要。
针对内存管理,Flink 实现了自身管理内存的机制,尽可能减少 JVM GC 对系统的影响。 另外,Flink 通过序列化/反序列化方法将所有的数据对象转换成二进制在内存中存储,降低数据存储的大小的同时,能够更加有效地对内存空间进行利用,降低 GC 带来的性能下降或任务异常的风险,
因此 Flink 较其他分布式处理的框架会显得更加稳定,不会因为 JVM GC 等问题而影响整个应用的运行。

7.Save Points(保存点) 对于 7*24 小时运行的流式应用,数据源源不断地接入,在一段时间内应用的终止有可能导致数据的丢失或者计算结果的不准确,
例如进行集群版本的升级、停机运维操作等操作。
值得一提的是,Flink 通过 Save Points 技术将任务执行的快照保存在存储介质上,当任务重启的时候可以直接从事先保存的 Save Points 恢复原有的计算状态,
使得任务继续按照停机之前的状态运行,Save Points 技术可以让用户更好地管理和运维实时流式应用

# 2.基础概念

# 2.1 处理无界和有界数据

任何类型的数据都可以形成一种事件流。信用卡交易、传感器测量、机器日志、网站或移动应用程序上的用户交互记录,所有这些数据都形成一种流。

数据可以被作为 无界 或者 有界 流来处理。

1.无界流 有定义流的开始,但没有定义流的结束。它们会无休止地产生数据。
无界流的数据必须持续处理,即数据被摄取后需要立刻处理。我们不能等到所有数据都到达再处理,因为输入是无限的,在任何时候输入都不会完成。
处理无界数据通常要求以特定顺序摄取事件,例如事件发生的顺序,以便能够推断结果的完整性。

2.有界流 有定义流的开始,也有定义流的结束。
有界流可以在摄取所有数据后再进行计算。有界流所有数据可以被排序,所以并不需要有序摄取。有界流处理通常被称为批处理

# 2.2 部署应用

Hadoop YARN、 Apache Mesos 和 Kubernetes,同时也可以作为独立集群运行。

# 2.3 利用内存性能

有状态的 Flink 程序针对本地状态访问进行了优化。任务的状态始终保留在内存中,如果状态大小超过可用内存,则会保存在能高效访问的磁盘数据结构中。
任务通过访问本地(通常在内存中)状态来进行所有的计算,从而产生非常低的处理延迟。
Flink 通过定期和异步地对本地状态进行持久化存储来保证故障场景下精确一次的状态一致性。

# 3.流处理应用基本组件介绍

# 3.1 流

(数据)流是流处理的基本要素。然而,流也拥有着多种特征。这些特征决定了流如何以及何时被处理。Flink 是一个能够处理任何类型数据流的强大处理框架。

有界无界 的数据流:流可以是无界的;也可以是有界的。
例如固定大小的数据集。Flink 在无界的数据流处理上拥有诸多功能强大的特性,同时也针对有界的数据流开发了专用的高效算子。

实时历史记录 的数据流:所有的数据都是以流的方式产生。数据生成时进行实时的处理;亦或是先将数据流持久化到存储系统中——例如文件系统或对象存储,然后再进行批处理。
Flink 的应用能够同时支持处理实时以及历史记录数据流。

# 3.2 状态

只有在每一个单独的事件上进行转换操作的应用才不需要状态,换言之,每一个具有一定复杂度的流处理应用都是有状态的。

例如:以下 JSON 表示一个消息的消费情况,对于当前未消费的数据可以在当前单独数据中做减法计算得出,所以是不需要状态的

{
  "timestamp": 1555516800,
  "offset": {
    "producer": 15,  // 生产者生产了 15 条消息
    "consumer0": 10, // 消费者 0 消费了 10 条,5 条未消费
    "consumer1": 7   // 消费者 1 消费了 7 条,8 条未消费
  }
}

例如:以下 JSON 表示一个 URL 访问情况,我们实时计算每个 URL 访问次数时,每个 URL 访问都是基于当前访问次数+1,所以是需要状态的

[
  {
    "timestamp": 1555516800,
    "url": "/api/a"
  },
  {
    "timestamp": 1555516801,
    "url": "/api/b"
  },
  {
    "timestamp": 1555516802,
    "url": "/api/a"
  }
]
 // => 输出:
[
  {
    "url": "/api/a",
    "count": 2
  },
  {
    "url": "/api/b",
    "count": 1
  }
]

任何运行基本业务逻辑的流处理应用都需要在一定时间内存储所接收的事件或中间结果,以供后续的某个时间点(例如收到下一个事件或者经过一段特定时间)进行访问并进行后续处理。 应用状态是 Flink 中的一等公民,Flink 提供了许多状态管理相关的特性支持,其中包括:

  • 多种状态基础类型:Flink 为多种不同的数据结构提供了相对应的状态基础类型,例如原子值(value),列表(list)以及映射(map)。开发者可以基于处理函数对状态的访问方式,选择最高效、最适合的状态基础类型。
  • 插件化的 State Backend:State Backend 负责管理应用程序状态,并在需要的时候进行 checkpoint。Flink 支持多种 state backend,可以将状态存在内存或者 RocksDB。RocksDB 是一种高效的嵌入式、持久化键值存储引擎。Flink 也支持插件式的自定义 state backend 进行状态存储。
  • 精确一次语义:Flink 的 checkpoint 和故障恢复算法保证了故障发生后应用状态的一致性。因此,Flink 能够在应用程序发生故障时,对应用程序透明,不造成正确性的影响。
  • 超大数据量状态:Flink 能够利用其异步以及增量式的 checkpoint 算法,存储数 TB 级别的应用状态。
  • 可弹性伸缩的应用:Flink 能够通过在更多或更少的工作节点上对状态进行重新分布,支持有状态应用的分布式的横向伸缩。

# 3.3 时间

时间是流处理应用另一个重要的组成部分。
因为事件总是在特定时间点发生,所以大多数的事件流都拥有事件本身所固有的时间语义。
进一步而言,许多常见的流计算都基于时间语义,例如窗口聚合、会话计算、模式检测和基于时间的 join。
流处理的一个重要方面是应用程序如何衡量时间,即区分事件时间(event-time)和处理时间(processing-time)。

Flink 提供了丰富的时间语义支持

  • 事件时间模式:使用事件时间语义的流处理应用根据事件本身自带的时间戳(event-time)进行结果的计算。
    因此,无论处理的是历史记录的事件还是实时的事件,事件时间模式的处理总能保证结果的准确性和一致性。
  • Watermark 支持:Flink 引入了 watermark 的概念,用以衡量事件时间进展。Watermark 也是一种平衡处理延时和完整性的灵活机制。
  • 迟到数据处理:当以带有 watermark 的事件时间模式处理数据流时,在计算完成之后仍会有相关数据到达。这样的事件被称为迟到事件。
    Flink 提供了多种处理迟到数据的选项,例如将这些数据重定向到旁路输出(side output)或者更新之前完成计算的结果。
  • 处理时间模式:除了事件时间模式,Flink 还支持处理时间语义。
    处理时间模式根据处理引擎的机器时钟触发计算,一般适用于有着严格的低延迟需求,并且能够容忍近似结果的流处理应用。

# 3.4 分层 API

  1. SQL & Table API(高阶 API,不区分流批模式)
  2. DataStream API(流计算)& DataSet API(批计算)
  3. ProcessFunction(底层 API 接口)

# 3.5 库

Flink 具有数个适用于常见数据处理应用场景的扩展库。这些库通常嵌入在 API 中,且并不完全独立于其它 API。它们也因此可以受益于 API 的所有特性,并与其他库集成。

  • 复杂事件处理 (CEP):模式检测是事件流处理中的一个非常常见的用例。Flink 的 CEP 库提供了 API,使用户能够以例如正则表达式或状态机的方式指定事件模式。
    CEP 库与 Flink 的 DataStream API 集成,以便在 DataStream 上评估模式。CEP 库的应用包括网络入侵检测,业务流程监控和欺诈检测。

  • DataSet API:DataSet API 是 Flink 用于批处理应用程序的核心 API。
    DataSet API 所提供的基础算子包括 map、reduce、(outer) join、co-group、iterate 等。
    所有算子都有相应的算法和数据结构支持,对内存中的序列化数据进行操作。如果数据大小超过预留内存,则过量数据将存储到磁盘。
    Flink 的 DataSet API 的数据处理算法借鉴了传统数据库算法的实现,例如混合散列连接(hybrid hash-join)和外部归并排序(external merge-sort)。

  • Gelly: Gelly 是一个可扩展的图形处理和分析库。Gelly 是在 DataSet API 之上实现的,并与 DataSet API 集成。
    因此,它能够受益于其可扩展且健壮的操作符。Gelly 提供了内置算法,如 label propagation、triangle enumeration 和 page rank 算法,也提供了一个简化自定义图算法实现的 Graph API。

# 4.应用场景

# 4.1 事件驱动型应用

事件驱动型应用是一类具有状态的应用,它从一个或多个事件流提取数据,并根据到来的事件触发计算、状态更新或其他外部动作。
事件驱动型应用是在计算存储分离的传统应用基础上进化而来。在传统架构中,应用需要读写远程事务型数据库。
相反,事件驱动型应用是基于状态化流处理来完成。在该设计中,数据和计算不会分离,应用只需访问本地(内存或磁盘)即可获取数据。
系统容错性的实现依赖于定期向远程持久化存储写入 checkpoint。下图描述了传统应用和事件驱动型应用架构的区别。 https://flink.apache.org/img/usecases-eventdrivenapps.png

# 4.1.1 事件驱动型应用的优势?

事件驱动型应用无须查询远程数据库,本地数据访问使得它具有更高的吞吐和更低的延迟。
而由于定期向远程持久化存储的 checkpoint 工作可以异步、增量式完成,因此对于正常事件处理的影响甚微。
事件驱动型应用的优势不仅限于本地数据访问。传统分层架构下,通常多个应用会共享同一个数据库,因而任何对数据库自身的更改(例如:由应用更新或服务扩容导致数据布局发生改变)都需要谨慎协调。
反观事件驱动型应用,由于只需考虑自身数据,因此在更改数据表示或服务扩容时所需的协调工作将大大减少。

# 4.1.2 如何支持事件驱动型应用

事件驱动型应用会受制于底层流处理系统对时间和状态的把控能力,Flink 诸多优秀特质都是围绕这些方面来设计的。
它提供了一系列丰富的状态操作原语,允许以精确一次的一致性语义合并海量规模(TB 级别)的状态数据。
此外,Flink 还支持事件时间和自由度极高的定制化窗口逻辑,而且它内置的 ProcessFunction 支持细粒度时间控制,方便实现一些高级业务逻辑。
同时,Flink 还拥有一个复杂事件处理(CEP)类库,可以用来检测数据流中的模式。

Flink 中针对事件驱动应用的明星特性当属 Savepoint
Savepoint 是一个一致性的状态映像,它可以用来初始化任意状态兼容的应用。
在完成一次 Savepoint 后,即可放心对应用升级或扩容,还可以启动多个版本的应用来完成 A/B 测试。

# 4.1.3 典型的事件驱动型应用实例

  • 反欺诈
  • 异常检测
  • 基于规则的报警
  • 业务流程监控
  • (社交网络)Web 应用

# 4.2 数据分析应用

数据分析任务需要从原始数据中提取有价值的信息和指标。
传统的分析方式通常是利用批查询,或将事件记录下来并基于此有限数据集构建应用来完成。
为了得到最新数据的分析结果,必须先将它们加入分析数据集并重新执行查询或运行应用,随后将结果写入存储系统或生成报告。 如下图所示,Apache Flink 同时支持流式及批量分析应用。
https://flink.apache.org/img/usecases-analytics.png

# 4.2.1 典型的数据分析应用实例

  • 电信网络质量监控
  • 移动应用中的产品更新及实验评估分析
  • 消费者技术中的实时数据即席分析
  • 大规模图分析

# 4.3 数据管道应用

提取-转换-加载(ETL)是一种在存储系统之间进行数据转换和迁移的常用方法。ETL 作业通常会周期性地触发,将数据从事务型数据库拷贝到分析型数据库或数据仓库。

数据管道和 ETL 作业的用途相似,都可以转换、丰富数据,并将其从某个存储系统移动到另一个。但数据管道是以持续流模式运行,而非周期性触发。
因此它支持从一个不断生成数据的源头读取记录,并将它们以低延迟移动到终点。
例如:数据管道可以用来监控文件系统目录中的新文件,并将其数据写入事件日志;另一个应用可能会将事件流物化到数据库或增量构建和优化查询索引。

下图描述了周期性 ETL 作业和持续数据管道的差异。

# 4.3.1 典型的数据管道应用实例

  • 电子商务中的实时查询索引构建
  • 电子商务中的持续 ETL
最后修改时间: 2/17/2020, 4:43:04 AM