1.3 Flink的核心特性与架构
前文介绍了Flink的应用场景,我们已经了解到,正是由于Flink拥有一些特性,某些应用和数据架构的实现才成为可能。本节将简单介绍Flink的核心特性和架构。
1.3.1 核心特性
了解Flink的核心特性有助于阅读源码时把握重点。Flink包括以下核心特性。
● 批流一体化。Flink可以在底层用同样的数据抽象和计算模型来进行批处理和流处理。事实上,Flink在设计理念上没有刻意强调批处理或流处理,而更多地强调数据的有界或无界。这个特性在企业技术选型中具有举足轻重的作用,因为这意味着如果Flink能够满足业务需求,就无须用两种甚至多种框架分别实现批处理和流处理,这大大降低了架构设计、开发、运维的复杂度,可以节省大量人力成本。
● 支持有状态计算。从产品的角度来看,Flink最大的“卖点”就是它支持有状态计算,这是实现前文介绍的事件驱动型应用、数据分析型应用等的基础。正如Flink官网首页上介绍的那样——Flink是数据流上的有状态计算。
● 提供多种时间语义。在流处理中,数据到达Flink系统的顺序很可能与事件本身发生的顺序不同,这就是流处理中常见的数据乱序现象。针对这个问题,Flink中区分了事件时间和处理时间:前者表示事件发生的时间,一般从数据自带的时间戳字段提取;后者表示数据被Flink处理的系统时间。当Flink选用事件时间对数据进行处理时,可以对数据进行排序等操作,从而得到准确的结果;当Flink选用处理时间对数据进行处理时,虽然不一定能得到准确的结果,但可以满足低延迟需求。多种时间语义使Flink可以在不同的需求实现间达到平衡。
● 轻量级分布式快照。既然Flink支持有状态计算,那么同时提供对状态的持久化功能就能实现容错机制。Flink提供了检查点(checkpoint)机制和相关组件,可实现状态存储与恢复,其最大的特点是,存储状态的操作过程是十分轻量的分布式过程。
● 支持多种一致性语义。Flink可以精确地满足系统内部的“至少一次”(at least once)语义和“恰好一次”(exactly once)语义。在外部系统的配合下,Flink也可以比较容易地实现端到端的“恰好一次”行为。
● 多层级API。Flink为用户提供了多个层级的API,用户可以根据自身对于表达力和易用性的需求来选择。不同层级的API可以混用,以实现复杂的业务逻辑。
Flink的特性远不止上面介绍的这些,还包括丰富的连接器、多平台部署等。但主要是上面介绍的这些特性让Flink实现了相对于其他框架的差异化,并深刻地影响了Flink未来的发展方向。
1.3.2 架构
图1-8所示为Flink对应各层级结构的组件架构。
图1-8 Flink组件架构
如果想利用Flink进行业务开发,那么将重点放在API层即可。要想学习Flink的底层原理,对源码进行定制化开发,则必须深入学习API层下面的层级。
图1-8中,最下层为部署层,Flink提供了多种部署模式,在不同的部署模式之上提供的是相同的运行时架构。
运行时层可以说是Flink组件架构中最重要的一层,大部分概念和核心操作定义都在这一层,包括执行图的生成、作业的调度与部署、数据的处理和交换等。这里先对运行时架构和其中的基本概念进行简单介绍,本书后文会对运行时层的各个环节展开分析。
与大部分分布式架构一样,Flink采用的也是“主从架构”。其中,“主”指作业管理器,负责执行图的生成、作业的调度与部署等;“从”指任务管理器,负责任务的执行、数据的交换等。运行时架构如图1-9所示。
图1-9 运行时架构
在该架构中,客户端(Client)负责向作业管理器(JobManager)提交作业,从而生成执行图;JobManager负责任务(Task)的调度和部署,与任务管理器(TaskManager)通信,将任务派发到TaskManager中;TaskManager根据资源情况将任务放在各个任务槽(TaskSlot)中执行,并向JobManager汇报任务状态等信息。任务执行过程中会涉及上下游任务的数据交换,这个过程发生在TaskManager内部或TaskManager之间。
图1-9所示的只是一个简化版的运行时架构,但它也基本涵盖Flink运行时的核心组件和流程。学习Flink源码的过程实际上就是逐步理解该架构的过程。比如,可以针对图1-9提出如下问题。
● 执行图是如何生成的?
● 任务是如何拆分的?
● 调度器(scheduler)是如何调度和部署这些任务的?
● TaskManager是如何划分资源的?
● 任务是如何在TaskManager中执行的?
● 任务的数据是如何交换的?
JobManager与TaskManager之间还存在其他交互,如有关检查点机制的流程中涉及的通信等。这些问题会在阅读后文后一一得到解决。