近年来,Apache Flink已经确立了自己作为实时流处理的事实标准。流处理是一种系统构建的范例,将事件流(时间序列的事件)视为其最基本的构建块。流处理器(如Flink)消耗由事件源产生的输入流,并产生由源(源存储结果并使其可供进一步处理使用)消费的输出流。
像亚马逊、网飞和优步这样的知名企业都依赖Flink来驱动在其业务核心巨大规模的数据管道,但Flink在许多有类似需求的小公司中也发挥着关键作用,以便能够迅速对关键业务事件作出反应。
Flink被用于什么?常见的用例分为这三类:
流数据管道 |
实时分析 |
事件驱动应用程序 |
---|---|---|
持续摄取、丰富和转换数据流,将它们加载到目标系统中以便及时采取行动(与批处理相比)。 |
持续生成和更新结果,这些结果在用户消费实时数据流时展示并传递给用户。 |
识别模式并对传入事件作出反应,通过触发计算、状态更新或外部操作。 |
一些例子包括:
|
一些例子包括:
|
一些例子包括:
|
Flink包括:
- 为全球企业所需的大规模数据流工作负载提供强大的支持
- 强制的确一次正确性和故障恢复保证
- 支持Java,Python和SQL,并统一支持批处理和流处理
- Flink是Apache软件基金会的一个成熟的开源项目,并且有一个非常活跃和支持的社区。
Flink有时被描述为复杂和难以学习。是的,Flink的运行时实现是复杂的,但这并不令人惊讶,因为它解决了一些难题。Flink API可能有点难以学习,但这更多地与概念和组织原则不熟悉有关,而不是任何固有的复杂性。
Flink可能与您之前使用过的任何东西都不同,但在很多方面,它实际上是非常简单的。随着您对Flink的组成方式以及其运行时必须解决的问题变得更加熟悉,Flink的API细节应该开始让您觉得是几个关键原则的明显后果,而不是您需要记忆的神秘细节。
本文旨在通过阐述其设计背后的核心原则,使Flink的学习之旅变得更容易。
Flink体现了一些大的理念
流
Flink是一个构建处理事件流应用程序的框架,其中流是一系列有界或无界的事件序列。
Flink应用程序是一个数据处理管道。您的事件通过这个管道流动,并且在您编写的每个阶段的代码中进行操作。我们将这个管道称为作业图,这个图的节点(换句话说,处理管道的阶段)称为操作符。
您使用Flink的一个API编写的代码描述了作业图,包括操作符的行为和它们的连接。
并行处理
每个操作符都可以有多个并行实例,每个实例独立地处理事件的一部分。
有时您可能希望对这些子流施加特定的分区方案,以便根据某些特定于应用程序的逻辑将事件分组在一起。例如,如果您处理财务交易,您可能需要为每个给定交易中的每个事件安排同一个线程进行处理。这将允许您将每个交易随时间发生的相关事件连接在一起
在Flink SQL中,您可以通过使用GROUP BY transaction_id
来实现此目的,而在DataStream API中,您可以使用keyBy(event -> event.transaction_id)
来指定这种分组或分区。无论哪种情况,这都会在作业图中显示为两个连续阶段的完全连接网络交换。
状态
在键分区流上工作的操作符可以使用Flink的分布式键/值状态存储来持久化他们想要保存的任何数据。每个键的状态仅限于操作符的特定实例,并且不能从其他任何地方访问。并行子拓扑之间毫无共享——这对于无限制的可伸缩性至关重要。
Flink作业可能会无限期地运行。如果Flink作业持续为每个新键(例如,交易ID)创建新键并存储每个新键的数据,那么该作业有爆炸的风险,因为它使用了无界的状态量。Flink的每个API都是围绕提供帮助您避免状态无限增长的方法来组织的。
时间
避免长时间占用状态的一种方法是,只保留状态到某个特定的时间点。例如,如果您想以分钟为单位计算事务,那么一旦每分钟结束,就可以生成该分钟的结果,并释放该计数器。
Flink 对两种不同的时间概念进行了重要区分:
- 处理(或挂钟)时间,它来源于处理事件时的实际时间
- 事件时间,它基于每个事件记录的时间戳
为了说明它们之间的区别,请考虑一分钟长的窗口完成的含义:
- 当一分钟结束时,处理时间窗口完成。
- 当一分钟内发生的所有事件都已处理完毕时,事件时间窗口即完成。这可能很棘手,因为 Flink 对尚未处理的事件一无所知。我们所能做的最好的办法就是假设流可能会失序,并启发式地应用该假设。
故障恢复的检查点
故障是不可避免的。尽管存在故障,Flink 仍能提供有效的精确一次保证,这意味着每个事件都将影响 Flink 正在管理的状态一次,就像故障从未发生过一样。Flink 通过对所有状态进行定期、全局、自一致的快照来实现这一点。这些由 Flink 自动创建和管理的快照被称为检查点。
恢复涉及回滚到最新检查点捕获的状态,并从该检查点重新启动所有操作员。在恢复过程中,一些事件会被重新处理,但Flink能够通过确保每个检查点是系统的完整状态的全球一致性快照来保证正确性。
系统架构
Flink应用程序在Flink集群中运行,因此在将Flink应用程序投入生产之前,您需要一个集群来部署它。幸运的是,在开发和测试期间,通过在IntelliJ或Docker等集成开发环境(IDE)中本地运行Flink,很容易开始。
Flink集群有两种组件:作业管理器和一系列任务管理器。任务管理器并行运行您的应用程序,而作业管理器充当任务管理器与外部世界之间的网关。应用程序提交给作业管理器,它管理任务管理器提供的资源,协调检查点,并以指标的形式提供对集群的可见性。
开发者体验
作为Flink开发者的体验在一定程度上取决于您选择哪种API:较旧的、较低级的DataStream API或较新的、关系型的表和SQL API。
当你使用Flink的DataStream API编程时,你是在有意识地考虑Flink运行时在执行你的应用程序时会做什么。这意味着你是一次性构建作业图的,一次一个操作符,描述所使用的状态以及涉及的数据类型和它们的序列化,创建定时器,并实现当这些定时器触发时执行的回调函数等。DataStream API的核心抽象是事件,你编写的函数将一次处理一个事件,随着它们的到达。
另一方面,当你使用Flink的Table/SQL API时,这些底层问题已经被处理,你可以更直接地关注你的业务逻辑。核心抽象是表,你更多地考虑的是将表连接起来进行丰富,将行分组在一起以计算聚合分析等。内置的SQL查询计划器和优化器负责处理细节。计划/优化器在管理资源方面表现出色,通常优于手工编写的代码。
在深入细节之前,还有几点思考:首先,你不必选择DataStream或Table/SQL API – 这两个API是可互操作的,你可以将它们结合起来。如果你需要一些在Table/SQL API中不可能的定制,这可能是一个不错的选择。但是,超越Table/SQL API提供的功能的另一种好方法是以用户定义的函数(UDFs)的形式添加一些额外功能。在这方面,Flink SQL提供了很多扩展选项。
构建作业图
无论您使用哪个API,您编写代码的最终目的都是构建Flink运行时将代表您执行的作业图。这意味着这些API是围绕创建操作符和指定它们的行为以及彼此之间的连接组织的。使用DataStream API时,您直接构建作业图,而使用Table/SQL API时,Flink的SQL规划器来处理这个问题。
序列化和数据
最终,您提供给Flink的代码将由集群中的工作者(任务管理器)并行执行。为了实现这一点,您创建的功能对象被序列化并发送到任务管理器,然后在任务管理器中执行。同样,事件本身有时也需要被序列化,并从一台任务管理器通过网络发送到另一台。在这方面,使用Table/SQL API可以不必关心。
管理状态
Flink运行时需要了解在发生故障时您期望它为您恢复的任何状态。为了使其工作,Flink需要类型信息来序列化和反序列化这些对象(以便将它们写入并从检查点读取)。您可以可选地使用具有生存时间的描述符来配置这些托管状态,Flink将使用它自动删除一旦超出其使用寿命的状态。
使用DataStream API时,您通常直接管理应用程序所需的状态(内置的窗口操作是这个规则的一个例外)。另一方面,使用Table/SQL API,这个关注点被抽象化了。例如,给出下面的查询,您知道在Flink运行时某个地方必须维护一个针对每个URL的计数器,但所有细节都为您处理好了。
SELECT url, COUNT(*)
FROM pageviews
GROUP BY URL;
设置和触发定时器
在流处理中,定时器有许多用途。例如,Flink应用程序经常需要从许多不同的事件源收集信息,最终产生结果。定时器适用于那些可以等待(但不是无限期)可能(或可能不)最终到达的数据的情况。
定时器也是实现基于时间的窗口操作的关键。DataStream和Table/SQL API都有内置的对窗口的支持,并且正在为您创建和管理定时器。
用例
回到本文开头介绍的流处理的三类用例,让我们看看它们如何与Flink相关联。
流数据管道
在左侧,以下是传统批量提取、转换和加载(ETL)作业的示例,它定期从事务型数据库中读取数据,转换数据,并将结果写入另一个数据存储,如数据库、文件系统或数据湖。
相应的流管道在表面上相似,但有一些显著的区别:
- 流式管道一直在运行。
- 事务数据以两部分的形式传递给流式管道:第一部分是从数据库开始的初始大批量加载,结合变更数据捕获(CDC)流,该流携带自大批量加载以来的数据库更新。
- 流式版本一旦可用就不断产生新结果。
- 状态是显式管理的,以便在发生故障时能够健壮地恢复。流式ETL管道通常使用非常少的状态。数据源精确跟踪已经摄取的输入内容,通常以偏移量的形式,计算从流开始记录的数量。汇出系统(如数据库或Kafka)使用事务管理其写入操作。在检查点过程中,数据源记录它们的偏移量,汇出系统提交事务,这些事务携带的结果是恰好读取到,但不超出这些源偏移量的数据。
对于这个用例,Table/SQL API将是一个不错的选择。
实时分析
与流式ETL应用程序相比,这个流式分析应用程序有几个有趣的区别:
- 再次,Flink用于运行连续应用程序,但对于这个应用程序,Flink可能需要管理大量的状态。
- 对于这个用例,流式摄取的流存储在流本地存储系统中,如Apache Kafka,是有意义的。
- 而不是定期生成静态报告,可以使用流式版本来驱动一个实时仪表板。
再次,对于这个用例,Table/SQL API通常是一个不错的选择。
事件驱动应用程序
我们第三种也是最后一种用例涉及事件驱动应用程序或微服务的实现。关于这个话题,已有许多其他方面的论述;这是一种具有许多好处的架构设计模式。
Flink 对于这些应用程序来说是一个很好的选择,特别是如果您需要 Flink 能够提供的性能。在某些情况下,Table/SQL API 拥有您所需的一切,但在许多情况下,您至少需要使用 DataStream API 的额外灵活性来完成部分工作。
开始使用 Flink
Flink 为构建处理事件流的应用程序提供了一个强大的框架。正如我们所涵盖的,一些概念可能一开始看起来很新奇,但一旦您熟悉了 Flink 的设计和运作方式,这个软件就容易使用了,而且掌握 Flink 的好处也是显而易见的。
作为下一步,请遵循Flink 文档中的说明,它将引导您通过下载、安装和运行最新稳定版 Flink 的过程。考虑我们讨论的广泛用例 – 现代数据管道、实时分析和事件驱动微服务 – 以及这些如何帮助解决您的组织的挑战或创造价值。
数据流处理是当今企业技术中最激动人心的领域之一,而使用Flink进行流处理使它变得更加强大。学习Flink不仅对你的组织有益,对你的职业生涯也是如此,因为实时数据处理对全球企业来说变得越来越有价值。所以今天就来了解一下Flink吧,看看这项强大的技术能帮助你实现什么。
Source:
https://dzone.com/articles/apache-flink-101-a-guide-for-developers