Wait the light to fall

学习 Flink: 实践培训

焉知非鱼

Learn Flink: Hands-on Training

本次培训的目标和范围 #

本培训介绍了 Apache Flink,包括足够的内容让你开始编写可扩展的流式 ETL,分析和事件驱动的应用程序,同时省略了很多(最终重要的)细节。本书的重点是为 Flink 管理状态和时间的 API 提供直接的介绍,希望在掌握了这些基础知识后,你能更好地从更详细的参考文档中获取其余需要了解的内容。每一节末尾的链接将引导你到可以学习更多知识的地方。

具体来说,您将学习:

  • 如何实现流数据处理管道
  • Flink 如何以及为何管理状态
  • 如何使用事件时间来持续计算准确的分析结果?
  • 如何在连续流上构建事件驱动的应用程序?
  • Flink 是如何提供具有精确只读语义的容错、有状态的流处理的?

本培训主要介绍四个关键概念:流数据的连续处理、事件时间、有状态的流处理和状态快照。本页介绍了这些概念。

注: 伴随本培训的是一套实践练习,它将指导您学习如何使用所介绍的概念。每一节的最后都提供了相关练习的链接。

流处理 #

流是数据的天然栖息地。无论是来自网络服务器的事件,还是来自股票交易所的交易,或者是来自工厂车间机器的传感器读数,数据都是作为流的一部分被创建的。但当你分析数据时,你可以围绕有界流或无界流组织处理,而你选择哪种范式会产生深远的影响。

img

当你处理一个有边界的数据流时,批处理是工作的范式。在这种操作模式下,你可以选择在产生任何结果之前摄取整个数据集,这意味着,例如,可以对数据进行排序,计算全局统计,或产生一个汇总所有输入的最终报告。

另一方面,流处理涉及无边界的数据流。至少在概念上,输入可能永远不会结束,因此你不得不在数据到达时持续处理数据。

在 Flink 中,应用程序由流式数据流组成,这些数据流可以通过用户定义的运算符进行转换。这些数据流形成有向图,从一个或多个源开始,到一个或多个 sink 结束。

img

通常,程序中的变换(transformation)和数据流(dataflow)中的运算符(operator)之间存在一对一的对应关系。但有时,一个变换可能由多个运算符(operator)组成。

一个应用程序可能会消耗来自流式源的实时数据,如消息队列或分布式日志,如 Apache Kafka 或 Kinesis。但 Flink 也可以消耗来自各种数据源的有界历史数据。同样,Flink 应用正在产生的结果流也可以被发送到各种各样的系统,这些系统可以作为 sink 连接。

img

并行数据流 #

Flink 中的程序本质上是并行和分布式的。在执行过程中,一个流有一个或多个流分区(stream partitions),每个运算符(operator)有一个或多个运算符子任务(operator subtasks)。运算符子任务(operator subtasks)相互独立,在不同的线程中执行,也可能在不同的机器或容器上执行。

运算符符子任务(operator subtasks)的数量就是该特定运算符(operator)的并行度(parallelism)。同一程序的不同运算符可能具有不同的并行度水平。

img

流可以在两个运算符之间以一对一(或转发)的模式或以重分发的模式传输数据。

  • 一对一的流(例如上图中 Source 和 map() 运算符之间)保留了元素的分区和排序。这意味着 map() 运算符的 subtask[1] 将看到与 Source 运算符的 subtask[1] 所产生的元素顺序相同的元素。

  • 重新分发流(如上面 map() 和 keyBy/window 之间,以及 keyBy/window 和 Sink 之间)会改变流的分区。每个运算符子任务(operator subtask)都会根据所选的转换将数据发送到不同的目标子任务。例如 keyBy()(通过散列键来重新分区)、broadcast() 或 rebalance()(随机重新分区)。在重分发交换中,元素之间的排序只在每一对发送和接收子任务中被保留(例如,map() 的 subtask[1] 和 keyBy/window 的 subtask[2])。因此,例如,上面显示的 keyBy/window 和 Sink 运算符之间的重新分发,引入了关于不同键的聚合结果到达 Sink 的顺序的非确定性。

及时的流处理 #

对于大多数流式应用来说,能够用处理实时数据的相同代码重新处理历史数据是非常有价值的–无论如何,都能产生确定性的、一致的结果。

此外,关注事件发生的顺序,而不是事件交付处理的顺序,并且能够推理出一组事件何时(或应该)完成也是至关重要的。例如,考虑电子商务交易,或金融贸易中涉及的一系列事件。

通过使用记录在数据流中的事件时间戳,而不是使用处理数据的机器的时钟,可以满足这些及时流处理的要求。

有状态的流处理 #

Flink 的操作可以是有状态的。这意味着一个事件的处理方式可以取决于之前所有事件的累积效果。状态可以用于一些简单的事情,例如计算每分钟的事件以显示在仪表板上,或者用于一些更复杂的事情,例如计算欺诈检测模型的功能。

一个 Flink 应用是在分布式集群上并行运行的。一个给定的运算符的各种并行实例将以不同的线程独立执行,一般来说,它们将在不同的机器上运行。

一个有状态运算符的并行实例集实际上是一个分片的键值存储。每一个并行实例负责处理一组特定键的事件,这些键的状态被保存在本地。

下图显示了一个作业(Job),在作业图(job graph)中的前三个运算符上运行的并行度为2,终止于一个并行度为 1 的 sink。第三个运算符是有状态的,你可以看到在第二个和第三个运算符之间发生了一个完全连接的网络洗牌。这是在通过一些键来对流进行分区,这样所有需要一起处理的事件,都会被一起处理。

img

状态总是在本地访问,这有助于 Flink 应用实现高吞吐量和低延迟。你可以选择将状态保存在 JVM 堆上,如果状态太大,也可以将其保存在有效组织的磁盘数据结构中。

img

通过状态快照进行容错 #

Flink 能够通过状态快照和流重放的组合,提供容错、精确的一次性语义。这些快照捕获了分布式管道的整个状态,记录了进入输入队列的偏移以及整个作业图(job graph)中因摄取了该点数据而产生的状态。当发生故障时,源会被重放,状态被恢复,并恢复处理。如上所述,这些状态快照是异步捕获的,不会妨碍正在进行的处理。

原文链接: https://ci.apache.org/projects/flink/flink-docs-release-1.11/learn-flink/