Apache Flink 功能强大,支持开发和运行多种不同种类的应用程序。它的主要特性包括:
Flink 不仅可以运行在包括 YARN、 Mesos、Kubernetes 在内的多种资源管理框架上,还支持在裸机集群上独立部署。在启用高可用选项的情况下,Flink 不存在单点失效问题。事实证明,Flink 已经可以扩展到数千核心,其状态数据可以达到 TB 级别,且仍能保持高吞吐、低延迟的特性。
世界各地有很多要求严苛的流处理应用都运行在 Flink 之上。
Flink 支持以下三种常见的应用模式:
事件驱动型应用是一类基于状态的应用,它从一个或多个事件流中接收数据,并根据事件的内容触发计算 (Computations)
、状态更新(state updates)
或其他外部动作。
事件驱动型应用是在 基于计算存储分离
的传统应用的基础上演化而来。
传统应用一般是从远端的 事务型数据库
中读写数据,而事件驱动型应用是基于对 状态流
的处理来实现。因此在设计上,事件驱动型应用的数据和计算不会分离,应用只需访问本地(内存或磁盘)即可获取数据。
在系统容错性方面,事件驱动型应用通过定期向远程持久化存储写入 checkpoint 来实现。
首先,事件驱动型应用无须查询远程数据库,仅访问本地数据使得它具有更高的吞吐和更低的延迟。
所谓的远程数据库,是指在分布式系统中,存储共享数据的数据库,因此会涉及到事务并发控制。而本地数据,可以是进程内存,也可以是被独享的本地存储。
至于为保证系统容错性,事件驱动型应用定期向远程持久化存储 checkpoint 的工作,可以异步、增量式的完成,因此对于正常事件处理的影响甚微。当然,事件驱动型应用的优势不仅限于本地数据访问。传统分层架构下,通常多个应用会共享同一个数据库,因而任何对数据库自身的更改(例如:由应用更新或服务扩容导致数据布局发生改变)都需要谨慎协调。反观事件驱动型应用,由于只需考虑自身数据,因此在更改数据表示或服务扩容时所需的协调工作将大大减少。
事件驱动型应用受制于底层 流处理器
对时间和状态的把控能力,Flink 诸多优秀特质都是围绕这些方面来设计的。它提供了一系列丰富的 状态原语
,允许以 Exactly-once 的一致性语义
合并海量规模(TB 级别)的状态数据。此外,Flink 还支持 event-time 和自由度极高的定制化窗口逻辑,而且它内置的 ProcessFunction 支持细粒度时间控制,方便实现一些高级业务逻辑。同时,Flink 还拥有一个复杂事件处理(CEP)类库,可以用来检测数据流中的模式。
Flink 中针对事件驱动应用的明星特性当属 savepoint。Savepoint 是一个一致性的状态镜像,它可以作为初始状态点被用于重新初始化应用。在完成一次 savepoint 后,即可放心对应用升级或扩容,还可以启动多个版本的应用来完成 A/B 测试。
典型的事件驱动型应用实例
数据分析任务需要从原始数据中提取有价值的信息和指标。传统的分析方式通常是利用批查询,或将事件记录下来并基于有限数据集构建应用来完成。为了对最新的数据进行分析,需要先将它们追加到已分析数据集中,然后并重新执行查询或运行应用,最后将结果写入数据库或生成报告。
借助一些先进的流处理引擎,数据分析也可以实时的进行。和传统模式下读取有限数据集的方式不同,流式分析会接入实时事件流,并持续产生和更新结果。这些结果数据可以持久化至外部存储,或者以内部状态的形式管理。可视化应用(Dashboard)能实时查询最新的状态信息。
与批量分析相比,流式分析不需要周期性的执行数据导入和查询,直接从事件流中获取信息,因此延迟更低。除此之外,批量查询还涉及到有关数据边界的定义问题,比如一次性导入多少条数据,多少时间执行一次等等,而流式分析则无须考虑该问题。
另一方面,流式分析应用的架构更简洁。批量分析的数据管线模块通常由多个独立部件组成,它们需要周期性地调度来实现数据的导入和查询。一旦某个组件出错将会影响流水线的后续步骤。而流式分析应用可以运行在 Flink 之类的流处理系统之上,由引擎来维护和管理从数据接入到结果的持续计算等的所有步骤,还可以使用底层引擎提供的故障恢复机制。
Flink 为流式分析和批量分析都提供了良好的支持。具体而言,它内置了一个符合 ANSI 标准的 SQL 接口,将批、流查询的语义统一起来。无论是静态的事件流还是实时事件流,相同 SQL 查询都会得到一致的结果。同时 Flink 还支持丰富的用户自定义函数,允许在 SQL 中执行定制化代码。如果还需进一步定制逻辑,可以利用 Flink DataStream API 和 DataSet API 进行更低层次的控制。此外,Flink 的 Gelly 库为基于批量数据集的大规模高性能图分析提供了算法和构建模块支持。
典型的数据分析应用实例
提取-转换-加载(ETL)是一种在存储系统之间进行数据转换和迁移的常用方法。ETL 作业通常会周期性地触发,将数据从 事务型数据库
中拷贝到 分析型数据库
或 数据仓库
。
数据管道和 ETL 作业的用途相似,都可以转换、丰富数据,并将其从某个存储系统移动到另一个。但数据管道是以 持续的
、流的
模式运行,而非周期性触发。因此它支持从一个不断产生数据的源头读取或接收记录,并将它们低延迟地移动到目的点。
例如:数据管道可以用来监控文件系统目录中的新文件,并将其数据写入事件日志;另一个应用可以将事件流持久化至数据库,或者增量地构建和优化索引。
和周期性 ETL 作业相比,持续数据管道可以明显降低将数据移动到目的端的延迟。此外,由于它能够持续消费和发送数据,因此用途更广,支持用例更多。
很多常见的数据转换和增强操作可以利用 Flink 的 SQL 接口(或 Table API)及用户自定义函数解决。如果数据管道有更高级的需求,可以选择更通用的 DataStream API 来实现。Flink 为多种数据存储系统(如:Kafka、Kinesis、Elasticsearch、JDBC数据库系统等)内置了连接器。同时它还可以将文件系统适配为持续的数据源,可用来监控目录变化,并以时间分区的方式写入文件。
典型的数据管道应用实例