第 3 章 Flink的设计与运行原理 近年来,流处理这种应用场景在企业中变得越来越频繁,由此带动了企业 数据架构开始由传统数据处理架构、大数据Lambda架构向流处理架构演变。 Flink就是一种具有代表性的开源流处理架构,具有十分强大的功能,它实现了 GoogleDataflow 流计算模型,是一种兼具高吞吐、低延迟和高性能的实时流计 算框架,并且同时支持批处理和流处理。Flink的主要特性包括批流一体化、精 密的状态管理、事件时间支持以及“精确一次”的状态一致性保障等。Flink不 仅可以运行在包括YARN 、Mesos、Kubernetes等在内的多种资源管理框架上, 还支持在裸机集群上独立部署。Flink目前已经在全球范围内得到了广泛的应 用,大量企业已经开始大规模使用Flink作为企业的分布式大数据处理引擎。 本章首先给出Flink简介,并探讨选择Flink的原因以及Flink的典型应用 场景;其次介绍Flink的统一数据处理、技术栈、工作原理、编程模型和应用程序 结构;最后介绍Flink中的数据一致性。 3.lnk简介 1 Fi Flink是Apache软件基金会的一个顶级项目,是为分布式、高性能、随时可用 以及准确的流处理应用程序打造的开源流处理框架,并且可以同时支持实时计算 和批量计算。Flink起源于Stratosphere项目,该项目是2010—2014 年由柏林工业 大学、柏林洪堡大学和哈索·普拉特纳研究所联合开展的,开始是做批处理,后来 转向了流计算。2014 年4月,Stratosphere代码被贡献给Apache软件基金会,成为 Apache软件基金会孵化器项目,并开始在开源大数据 行业内崭露头角。之后,团队的大部分创始成员离开大 学,共同创办了一家名为DataArtisans的公司,该公司 于2019 年1月被我国的阿里巴巴公司收购。在项目孵 化期间,为了避免与另外一个项目发生重名, Staophee被重新命名为Fik。在德语中,ln rtsrlnFik是 “快速和灵巧”的意思,使用这个词作为项目名称,可以 彰显流计算框架的速度快和灵活性强的特点。项目使 用一只棕红色的松鼠图案作为标志(见图3-1), 因为松图3- 1 Flink的标志 第3章Flink的设计与运行原理89 鼠具有灵活、快速的特点。 2014 年12 月,Flink成为Apache软件基金会顶级项目。目前,Flink是Apache软件 基金会的5个最大的大数据项目之一,在全球范围内拥有350 多位开发人员,并在越来越 多的企业中得到了应用。在国外,优步、网飞、微软和亚马逊等公司已经开始使用Flink。 在国内,包括阿里巴巴、美团、滴滴等在内的知名互联网企业,都已经开始大规模使用 Flink作为企业的分布式大数据处理引擎。在阿里巴巴,基于Flink搭建的平台于2016 年正 式上线,并从阿里巴巴的“搜索和推荐”这两大场景开始实现。目前,阿里巴巴所有的业 务,包括阿里巴巴所有子公司都采用了基于Flink搭建的实时计算平台,服务器规模已经 达到数万台,这种规模等级在全球范围内也是屈指可数的。阿里巴巴的Flink平台内部 积累起来的状态数据,已经达到PB 级别规模,每天在平台上处理的数据量已经超过万亿 条,在峰值期间可以承担超过4.72 亿次每秒的访问量,最典型的应用场景是阿里巴巴 “双11”大屏。 Flink具有十分强大的功能,可以支持不同类型的应用程序。Flink的主要特性包括 批流一体化、精密的状态管理、事件时间支持以及“精确一次”的状态一致性保障等。 Flink不仅可以运行在包括YARN 、Mesos、Kubernetes等在内的多种资源管理框架上, 还支持在裸机集群上独立部署。当采用YARN 作为资源调度管理器时,Flink计算平台 可以运行在开源的Hadoop集群之上,并使用HDFS 作为数据存储,因此,Flink可以和开 源大数据软件Hadoop实现无缝对接。在启用高可用选项的情况下,Flink不存在单点失 效问题。事实证明,Flink已经可以扩展到数千核心,其状态可以达到TB 级别规模,且 仍能保持高吞吐、低延迟的特性。世界各地有很多要求严苛的流处理应用都运行在 Flink上。 3.选择Flnk的原因 2i 数据架构设计领域正在发生一场变革,开始由传统数据处理架构、大数据Lambda架 构向流处理架构演变,在这种全新的架构中,基于流的数据处理流程被视为整个架构设计 的核心。这种转变把Flink推向了分布式计算框架这个舞台的中心,使它可以在现代数 据处理中扮演重要的角色。 3.1 传统数据处理架构 2. 传统数据处理架构的一个显著特点就是采用一个中心化的数据库系统,用于存储事 务性数据,如图3-2所示。例如,在一个企业内部,会存在ERP 系统、订单系统、CRM 系 统等,这些系统的数据一般都被存储在关系数据库中。这些数据反映了当前的业务状态, 如系统当前的登录用户数、网站当前的活跃用户数、每个用户的账户余额等。当应用程序 需要较新的数据时,都会访问这个中心数据库。 在应用的初期,这种传统数据处理架构的效率很高,在各大企业应用中成功服务了几 十年。但是,随着企业业务量的不断增大,数据库的负载开始不断增加,最终变得不堪重 负,而一旦数据库系统发生问题,整个业务系统就会受到严重影响。此外,采用传统数据 90Flink编程基础(Scala版) 图3-2传统数据处理架构 处理架构的系统,一般都拥有非常复杂的异常问题处理方法,当出现异常问题时,很难保 证系统还能够很好地运行。 3.2.2大数据Lambda架构 随着信息技术的普及和企业信息化建设步伐的加快,企业逐步认识到建立企业范围 内的统一数据存储的重要性,越来越多的企业建立了企业数据仓库。企业数据仓库有效 集成了来自不同部门、不同地理位置、具有不同格式的数据,为企业管理决策者提供了企 业范围内的单一数据视图,从而为综合分析和科学决策奠定了坚实的基础。 起初数据仓库主要借助于Oracle、SQLServer、MySQL 等关系数据库进行数据的存 储,但是,随着企业数据量的不断增长,关系数据库已经无法满足海量数据的存储需求。 因此,越来越多的企业开始构建基于Hadoop的数据仓库,并借助MapReduce、Spark等 分布式计算框架对数据仓库中的数据进行处理分析。但是,数据仓库中的数据一般都是 周期性进行加载,如每天一次、每周一次或者每月一次,这样就无法满足一些对实时性要 求较高的应用的需求。为此,业界提出了一套大数据Lambda架构方案来处理不同类型 的数据,从而满足企业不同应用的需求。如图3-3所示,大数据Lambda架构主要包含两 层,即批处理层和实时处理层。在批处理层中,采用MapReduce、Spark等技术进行批量 数据处理;而在实时处理层中,则采用Storm 、SparkStreaming等技术进行数据的实时 处理。 分开处理连续的实时数据和有限批次的批量数据,可以使系统构建工作变得更加简 单,这种架构在一定程度上解决了不同计算类型的问题。但是,这种做法将管理两套系统 的复杂性留给了系统用户,由于存在太多的框架,就会导致平台复杂度和运维成本过高, 因为在一套资源管理平台中管理不同类型的计算框架是比较困难的事情。 3.3 流处理架构 2. 作为一种新的选择,流处理架构解决了企业在大规模系统中遇到的诸多问题。以流 为基础的架构设计,让数据记录持续地从数据源流向应用程序,并在各个应用程序间持续 流动。不需要设置一个数据库来集中存储全局状态数据,取而代之的是共享且永不停止 的流数据,它是唯一正确的数据源,记录了业务数据的历史。 为了高效地实现流处理架构,一般需要设置消息传输层和流处理层(见图3-4)。消 第3章Flink的设计与运行原理91 图3-3大数据Lambda架构 息传输层从各种数据源采集连续事件产生的数据,并传输给订阅了这些数据的应用程序; 流处理层会持续地将数据在应用程序和系统间移动、聚合并处理事件,并在本地维持应用 程序的状态。这里的“状态”就是计算过程中产生的中间计算结果,在每次计算中,新的数 据进入流系统中,都是在中间状态结果的基础上进行计算,最终产生正确的计算结果。 图3- 4 流处理架构 流处理架构的核心是使各种应用程序互连在一起的消息队列,消息队列连接应用程 序,并作为新的共享数据源,这些消息队列取代了从前的大型集中式数据库。如图3-5所 示,流处理器从消息队列中订阅数据并加以处理,处理后的数据可以流向另一个消息队 列,这样,其他应用程序都可以共享流数据。有时处理后的数据会被存储到本地数据 库中。 流处理架构正在逐步取代传统数据处理架构和大数据Lambda架构,成为大数据处 理架构的一种新趋势。一方面,由于流处理架构中不存在一个大型集中式数据库,因此, 避免了传统数据处理架构中存在的“数据库不堪重负”的问题;另一方面,在流处理架构 中,批处理被看作流处理的一个子集,因此,就可以用面向流处理的框架进行批处理,这样 就可以用一个流处理框架来统一处理流计算和批量计算,避免了大数据Lambda架构中 存在的“多个框架难管理”的问题。 3.2.4 Flink是理想的流计算框架 流处理架构需要具备低延迟、高吞吐和高性能的特性,而目前从市场上已有的产品来 92Flink编程基础(Scala版) 图3-5流处理架构中的消息队列 看(见表3-1), 只有Flink可以满足要求。Storm 虽然可以做到低延迟,但是无法实现高 吞吐,也不能在故障发生时准确地处理计算状态。Spark的流计算组件SparkStreaming 通过采用微批处理方法实现了高吞吐和容错性,但是牺牲了低延迟和实时处理能力。 Spark的另一个流计算组件StructuredStreaming,包括微批处理和持续处理两种处理模 型。采用微批处理模型时,最快响应时间需要100ms,无法支持毫秒级别响应;采用持续 处理模型时,可以支持毫秒级别响应,但是只能做到“至少一次”的一致性,无法做到“精确 一次”的一致性。 表3- 1 不同流计算框架的对比 产品消息保证机制容错机制状态管理延时吞吐量 Storm 至少一次Acker机制无低低 SparkStreaming 精确一次基于RDD 的检查点基于DStream 中高 Flink 精确一次检查点基于操作低高 Flink实现了GoogleDataflow 流计算模型,是一种兼具高吞吐、低延迟和高性能的 实时流计算框架,并且同时支持批处理和流处理。此外,Flink支持高度容错的状态管 理,防止状态在计算过程中因为系统异常而出现丢失。因此,Flink就成为了能够满足流 处理架构要求的理想的流计算框架。 3.2.5 Flink的优势 Flink实现了GoogleDataflow 流式计算模型,与其他的流计算框架相比,Flink具有 突出的特点,它不仅是一个高吞吐、低延迟的计算引擎,同时还具备其他的高级特性,如支 持有状态的计算、状态管理、强一致性的语义,以及对消息乱序的处理等。 总体而言,Flink具有以下优势。 第3章Flink的设计与运行原理931.同时支持高吞吐、低延迟、高性能 对于分布式流计算框架,同时支持高吞吐、低延迟和高性能是非常重要的。但是,目 前在开源社区中,能够同时满足这3方面要求的流计算框架只有Flink。Storm可以做到 低延迟,但是无法实现高吞吐。SparkStreaming可以实现高吞吐和容错性,但是不具备 低延迟和实时处理能力。 2.同时支持流处理和批处理 在典型的大数据的业务场景下,数据业务最通用的做法是,选用批处理的技术处理全 量数据,采用流式计算处理实时增量数据。在绝大多数的业务场景下,用户的业务逻辑在 批处理和流处理中往往是相同的。但是,在Flink被推广普及之前,用户用于批处理和流 处理的两套计算引擎是不同的。因此,用户通常需要写两套代码。毫无疑问,这带来了一 些额外的负担和成本。因此,我们就希望能够有一套统一的大数据引擎技术,用户只需要 根据自己的业务逻辑开发一套代码。这样在各种不同的场景下,不管是全量数据还是增 量数据,或者实时处理,一套方案即可全部支持,这就是Flink诞生的背景和初衷。Flink 不仅擅长流处理,同时也能够很好地支持批处理。对于Flink,批量数据是流数据的一个 子集,批处理被视作一种特殊的流处理,因此,可以通过一套引擎来处理流数据和批量 数据。 3.高度灵活的流式窗口 在流计算中,数据流是无限的,无法直接进行计算,因此,Flink提出了窗口的概念, 一个窗口是若干元素的集合,流计算以窗口为基本单元进行数据处理。窗口可以是时间 驱动的(TimeWindow,如每30s),也可以是数据驱动的(CountWindow,如每100个元 素)。窗口可以分为翻滚窗口(TumblingWindow,无重叠)、滚动窗口(SlidingWindow, 有重叠)和会话窗口(SesionWindow )。 4.支持有状态计算 流计算分为无状态和有状态两种情况。无状态计算观察每个独立的事件,并根据最 后一个事件输出结果,Storm就是无状态的计算框架,每条消息来了以后,彼此都是独立 的,和前后都没有关系。有状态的计算则会基于多个事件输出结果。正确地实现有状态 计算,比实现无状态计算难得多。Flink就是可以支持有状态计算的新一代流处理系统。 Flink的有状态应用程序针对本地状态访问进行了优化。任务状态保留在内存中,但是 如果状态大小超过可用内存,则保存在访问高效的磁盘数据结构中。因此,任务通过访问 本地(通常是内存)状态来执行所有计算,从而产生非常低的处理延迟。 5.具有良好的容错性 当分布式系统引入状态时,就会产生“一致性”问题。一致性实际上是“正确性级别” 的另一种说法,也就是说,在成功处理故障并恢复之后得到的结果,与没有发生故障时得 94Flink编程基础(Scala版) 到的结果相比,前者有多正确。Storm 只能实现“至少一次”的容错性。SparkStreaming 虽然可以支持“精确一次”的容错性,但是,无法做到毫秒级的实时处理。Flink提供了容 错机制,可以恢复数据流应用到一致状态。该机制确保在发生故障时,程序的状态最终将 数据流中的每个记录只反映一次,即实现了“精确一次”的容错性。容错机制不断地创建 分布式数据流的快照,对于小状态的流式程序,快照非常轻量,可以高频率创建而对性能 影响很小。 6.具有独立的内存管理 Java本身提供了垃圾回收机制来实现内存管理,但是,在大数据面前,JVM 的内存结 构和垃圾回收机制往往会成为掣肘。所以,目前包括Flink在内的越来越多的大数据项 目开始自己管理JVM 内存,为的就是获得像C语言一样的性能以及避免内存溢出的发 生。Flink通过序列化或反序列化方法,将所有的数据对象转换成二进制在内存中存储, 这样做一方面降低了数据存储的空间,另一方面能够更加有效地利用内存空间,降低垃圾 回收机制带来的性能下降或任务异常风险。 7.支持迭代和增量迭代 对某些迭代而言,并不是单次迭代产生的下一次工作集中的每个元素都需要重新参 这种形式的迭代就 与下一轮迭代,有时只需要重新计算部分数据同时选择性地更新解集, 是增量迭代。增量迭代能够使得一些算法执行得更高效,它可以让算法专注于工作集中 的“热点”数据部分,这导致工作集中的绝大部分数据冷却得非常快,因此随后的迭代面对 的数据规模将会大幅缩小。Flink的设计思想主要来源于Hadoop、MPP 数据库和流计算 系统等,支持增量迭代计算,具有对迭代进行自动优化的功能。 3.lnk典型应用场景 3 Fi Flink典型应用场景包括事件驱动型应用、数据分析应用和数据流水线应用。 3.1 事件驱动型应用 3. 1. 事件驱动型应用简介 事件驱动型应用是一类具有状态的应用,它从一个或多个事件数据流中读取事件,并 根据到来的事件做出反应,包括触发计算、状态更新或其他外部动作等。事件驱动型应用 是在传统事务型应用设计基础上进化而来的。在传统事务型应用设计中,通常都具有独 立的计算和数据存储层,应用会从一个远程的事务数据库中读写数据。而事件驱动型应 用是建立在有状态流处理应用的基础之上的。在这种设计中,数据和计算不是相互独立 的层,而是放在一起的,应用只需访问本地(内存或磁盘)即可获取数据。系统容错性是通 过定期向远程持久化存储写入检查点来实现的。图3-6描述了传统事务型应用和事件驱 动型应用架构的区别。 第3章Flink的设计与运行原理95 图3-6传统事务型应用和事件驱动型应用架构的区别 典型的事件驱动型应用包括反欺诈、异常检测、基于规则的报警、业务流程监控、Web 应用(社交网络)等。 2.事件驱动型应用的优势 事件驱动型应用都是访问本地数据,而无须查询远程的数据库,因此无论是在吞吐量 方面,还是在延迟方面,都可以获得更好的性能。向一个远程的持久化存储周期性地写入 检查点,可以采用异步和增量的方式来实现,因此检查点对于常规的事件处理的影响是很 小的。事件驱动型应用的优势不仅限于本地数据访问。在传统事务型应用的分层架构 中,多个应用共享相同的数据库,是一个很常见的现象。因此,数据库的任何变化,如由于 一个应用的更新或服务的升级而导致数据布局的变化,都需要谨慎协调。由于每个事件 驱动型应用都只需要考虑自身的数据,对数据表示方式的改变或者应用的升级,都只需要 很少的协调工作。 3.Flink是如何支持事件驱动型应用的 一个流处理器如何能够很好地处理时间和状态,决定了事件驱动型应用的局限性。 Flink的许多优秀特性都是围绕这些方面进行设计的。它提供了丰富的状态操作原语, 可以管理大量的数据(可以达到TB级别),并且可以确保“精确一次”的一致性。Flink还 支持事件时间、高度可定制的窗口逻辑和细粒度的时间控制,这些都可以帮助实现高级的 商业逻辑。它还拥有一个复杂事件处理(ComplexEventProcesing,CEP)类库,可以用 来检测数据流中的模式。 Flink中针对事件驱动型应用的突出特性当数保存点(SavePoint)。保存点是一个 一致性的状态镜像,它可以作为许多相互兼容应用的一个初始化点。给定一个保存点以 后,就可放心地对应用进行升级或扩容,还可以启动多个版本的应用来完成A/B测试。 3.数据分析应用 3.2 1.数据分析应用简介 分析作业会从原始数据中提取信息,并得到富有洞见的观察。传统的数据分析通常 先对事件进行记录,然后在这个有界的数据集上执行批量查询。为了把最新的数据融入 96Flink编程基础(Scala版) 查询结果中,就必须把这些最新的数据添加到被分析的数据集中,然后重新运行查询。查 询的结果会被写入一个存储系统中,或者形成报表。 一个高级的流处理引擎,可以支持实时的数据分析。这些流处理引擎并非读取有限 的数据集,而是获取实时事件流,并连续产生和更新查询结果。这些结果或者被保存到一 个外部数据库中,或者作为内部状态被维护。仪表盘应用可以从这个外部的数据库中读 取最新的结果,或者直接查询应用的内部状态。 如图3-7所示,ApacheFlink同时支持批量及流式分析应用。 图3-7Flink同时支持批量及流式分析应用 典型的数据分析应用包括电信网络质量监控、移动应用中的产品更新及实验评估分 析、消费者技术中的实时数据即席分析、大规模图分析等。 2.流式分析应用的优势 与批量分析相比,连续流式分析的优势:一方面,由于消除了周期性的导入和查询, 因此从事件中获取洞察结果的延迟更低。此外,流式查询不需要处理输入数据中人为产 生的边界。 另一方面,流式分析具有更加简单的应用架构。一个批量分析流水线会包含一些独 立的组件来周期性地调度数据提取和查询执行。如此复杂的流水线,操作起来并非易事, 因为一个组件的失败就会直接影响到流水线中的其他步骤。相反,运行在一个高级流处 理器(如Flink)之上的流式分析应用,会把从数据提取到连续结果计算的所有步骤都整合 起来,因此,它就可以依赖底层引擎提供的故障恢复机制。 3.Flink是如何支持数据分析应用的 Flink可以同时支持批处理和流处理。Flink提供了一个符合ANSI规范的SQL接 口,它可以为批处理和流处理提供一致的语义。不管是运行在一个静态的数据集上,还是 运行在一个实时的数据流上,SQL查询都可以得到相同的结果。它还提供了丰富的用户 自定义函数,使得用户可以在SQL查询中执行自定义代码。如果需要进一步定制处理逻 辑,Flink的DataStreamAPI和DataSetAPI提供了更加底层的控制。此外,Flink的 Gely库为基于批量数据集的大规模高性能图分析提供了算法和构建模块支持。 第3章Flink的设计与运行原理973.3.3数据流水线应用 1.数据流水线应用简介 Extract-transform-load(ETL)是一个在存储系统之间转换和移动数据的常见方法。 通常,ETL 作业会被周期性地触发,从而把事务型数据库系统中的数据复制到一个分析 型数据库或数据仓库中。 数据流水线可以实现和ETL 类似的功能,它们可以转换、清洗数据,或者把数据从一 个存储系统转移到另一个存储系统中。但是,它们是以一种连续的流模式来执行的,而不 是周期性地触发。因此,当数据源中源源不断地生成数据时,数据流水线就可以把数据读 取过来,并以较低的延迟转移到目的地。例如,一个数据流水线可以对一个文件系统目录 进行监控,一旦发现有新的文件生成,就读取文件内容并写入事件日志中。再例如,将事 件流物化到数据库或增量构建和优化查询索引。 图3-8描述了周期性ETL 作业和持续数据流水线的差异。 图3- 8 周期性ETL 作业和持续数据流水线的差异 典型的数据流水线应用包括电子商务中的实时查询索引构建、电子商务中的持续 ETL 等。 2. 数据流水线应用的优势 相对于周期性ETL 作业,持续数据流水线的优势是,减少了数据转移过程的延迟。 此外,由于它能够持续消费和发送数据,因此用途更广,支持用例更多。 3.Flink如何支持数据流水线应用 Flink的SQL 接口(或者TableAPI)以及丰富的用户自定义函数,可以解决许多常 见的数据转换问题。通过使用更具通用性的DataStreamAPI,还可以实现具有更加强大 功能的数据流水线。Flink提供了大量的连接器,可以连接到各种不同类型的数据存储 系统,如Kafka、Kinesis、Elasticsearch和JDBC 数据库系统。同时,Flink提供了面向文 件系统的连续型数据源,可用来监控目录变化,并提供了数据槽(Sink), 支持以时间分区 的方式写入文件。 3.lnk的统一数据处理 4 Fi 根据数据的产生方式,可以把数据集分为两种类型:有界数据集和无界数据集(图3-9)。 有界数据集具有时间边界,在处理过程中数据一定会在某个时间范围内起始和结束, 98Flink编程基础(Scala版) 图3-9有界数据集和无界数据集 有可能是一小时,也有可能是一天内的交易数据。有界数据集的特点是,数据是静止不动 的,不会存在数据的追加操作。对有界数据集的数据处理方式称为批处理,例如首先将数 据从关系数据库或文件系统等系统中读取出来,其次在分布式系统内进行处理,最后再将 处理结果写入存储系统中,整个过程称为批处理过程。目前业界比较流行的分布式处理 框架Hadoop和Spark等,都是面向批处理的。 对于无界数据集,数据从开始生成就一直持续不断地产生新的数据,因此数据是没有 边界的,例如服务器信令、网络传输流、传感器信号数据、实时日志信息等。和批量数据处 理方式对应,对无界数据集的数据处理方式被称为流处理。流处理需要考虑处理过程中 数据的顺序错乱,以及系统容错等方面的问题,因此流处理系统的设计与实现的复杂度要 明显高于批处理系统。Storm 、SparkStreaming、Flink等分布式计算引擎是不同时期具 有代表性的流处理系统。 为了更形象、直观地理解无界数据集与有界数据集,可以分别把二者类比成池塘和江 河。对有界数据集进行计算时,就好比计算池塘中的鱼的数量,只需要把池塘中当前所有 的鱼都计算一次就可以了。那么当前时刻,池塘中有多少条鱼就是最终结果。对于无界 数据集的计算,类似于计算江河中的鱼,在奔流到海的过程中,每时每刻都会有鱼流过而 进入大海,那么计算鱼的数量就是持续追加的。 有界数据集与无界数据集是一个相对模糊的概念。对于有界数据集,如果数据一条 一条地经过处理引擎,那么也可以认为是无界的。反过来,对于无界数据集,如果每间隔 一分钟、一小时、一天进行一次计算,那么也可以认为这一段时间内的数据又相对是有界 的。所以,有界数据集与无界数据集的概念有时候是可以存在互换的,因此,学界和业界 也就开始追寻批、流统一的框架,Spark和Flink都属于能够同时支持批处理和流处理的 分布式计算框架。 对于Spark,它会使用一系列连续的微小批处理来模拟流处理,即它会在特定的时间 间隔内发起一次计算,而不是每条数据都触发计算,这就相当于把无界数据集切分为多个 第3章Flink的设计与运行原理99 小量的有界数据集。对于Flink,它把有界数据集看成无界数据集的一个子集,因此,将批 处理与流处理混合到同一套引擎当中,用户使用Flink引擎能够同时实现批处理与流处 理任务。 3.5Flink技术栈 Flink的发展越来越成熟,已经拥有了自己丰富的核心组件栈。Flink核心组件栈分 为3层(见图3-10):物理部署层、Runtime核心层和API&Libraries层。 图3-10 Flink核心组件栈 (1)物理部署层。Flink的底层是物理部署层。Flink可以采用Local模式运行,启 动本地单个JVM,也可以采用Standalone集群模式运行,还可以采用YARN集群模式运 行,或者也可以运行在GCE(谷歌云服务)和EC2(亚马逊云服务)上。 (2)Runtime核心层。该层主要负责对上层不同接口提供基础服务,也是Flink分布 式计算框架的核心实现层。该层提供了两套核心的API:DataStreamAPI(流处理)和 DataSetAPI(批处理)。 (3)API&Libraries层。作为分布式数据库处理框架,Flink同时提供了支撑批计算 和流计算的接口,同时,在此基础上抽象出不同的应用类型的组件库,如CEP(基于流处 理的事件处理库)、TableAPI&SQL(既可以基于流处理,也可以基于批处理的关系数据 库)、FlinkML(基于批处理的机器学习库)、Gely(基于批处理的图计算库)等。 这里需要说明的是,Flink虽然也构建了一个大数据生态系统,功能涵盖流处理、批 处理、SQL 、图计算和机器学习等,但是,它的强项仍然是流计算,Flink的图计算组件 Gely和机器学习组件FlinkML并不是十分成熟。因此,本书不介绍Gely和FlinkML, 将详细讲解DataStreamAPI 、DataSetAPI 、TableAPI&SQL和CEP等组件。 100Flink编程基础(Scala版) 3.6Flink工作原理 如图3-11所示,Flink系统主要由两个组件组成,分别为JobManager和 TaskManager,Flink架构也遵循主从(Master-Slave)架构设计原则,JobManager为 Master节点,TaskManager为Slave节点。Flink系统各组件的功能如下。 图3-11 Flink的体系架构及其工作原理 (1)JobClient:负责接收程序,解析和优化程序的执行计划,然后提交执行计划到 JobManager。这里执行的程序优化是将相邻的算子融合,形成“算子链”,以减少任务的 数量,提高TaskManager的资源利用率。 (2)JobManager:负责整个Flink集群任务的调度以及资源的管理,它从客户端中获 取提交的应用,然后根据集群中TaskManager上TaskSlot的使用情况,为提交的应用分 配相应的TaskSlot资源,并命令TaskManager启动从客户端中获取的应用。为了保证 高可用,一般会有多个JobManager进程同时存在,它们之间也是采用主从模式,一个进 程被选举为Leader,其他进程为Folower,在作业运行期间,只有Leader在工作, Folower是闲置的,一旦Leader死亡,就会引发一次选举,产生新的Leader继续处理作 业。JobManager除了调度任务,另一个主要工作就是容错,主要依靠检查点机制进行 容错。 (3)TaskManager:相当于整个集群的Slave节点,负责具体的任务执行和对应任务 在每个节点上的资源申请与管理。客户端通过将编写好的Flink应用编译打包,提交到 JobManager,然后JobManager会根据已经注册在JobManager中TaskManager的资源 情况,将任务分配给有资源的TaskManager节点,然后启动并运行任务。TaskManager 从JobManager接收需要部署的任务,然后使用Slot资源启动Task,建立数据接入的网 络连接,接收数据并开始数据处理。同时TaskManager之间的数据交互都是通过数据流 第3章Flink的设计与运行原理101 的方式进行的。 (4)Slot:Slot是TaskManager资源粒度的划分,每个TaskManager像一个容器一 样,包含一个或多个Slot,每个Slot都有自己独立的内存,所有Slot平均分配 TaskManager的内存。需要注意的是,Slot仅划分内存,不涉及CPU的划分,即CPU是 共享使用的。每个Slot可以运行多个任务,而且一个任务会以单独的线程来运行。 采用Slot设计主要有3个好处:①可以起到隔离内存的作用,防止多个不同作业的 任务竞争内存;②Slot的个数就代表了一个Flink程序的最高并行度,简化了性能调优的 过程;③允许多个任务共享Slot,提升了资源利用率。 (5)Task:是在算子的子任务进行链化之后形成的,一个作业中有多少个Task和算 子的并行度和链化的策略有关。 Flink系统的工作原理:在执行Flink程序时,Flink程序需要首先提交给JobClient, 然后,oinongr。Jongr负责协调资源分配和作业执 JbClet将作业提交给JbMaaebMaae 行,它首先要做的是分配所需的资源。资源分配完成后,任务将提交给相应的 TaskManager。在接收任务时,TaskManager启动一个线程以开始执行。执行到位时, TaskManager会继续向JobManager报告状态更改,可以有各种状态,例如开始执行、正 在进行或已完成。作业执行完成后,结果将发送回客户端(JobClient)。 3.lnk编程模型 7 Fi Flink提供了不同级别的抽象(见图3-12),以开发流处理或批处理作业。 图3-12 Flink编程模型 (1)在Flink编程模型中,最低级的抽象接口是有状态数据流处理接口。这个接口是 通过过程函数(ProcesFunction)被集成到DataStreamAPI中的。该接口允许用户自由 地处理来自一个或多个流中的事件,并使用一致的容错状态。另外,用户也可以通过注册 事件时间并处理回调函数的方法来实现复杂的计算。 (2)实际上,大多数应用并不需要上述的底层抽象,而是针对核心API进行编程,如 DataStreamAPI(有界或无界流数据)以及DataSetAPI(有界数据集)。这些API为数据 处理提供了大量的通用模块,如用户定义的各种各样的转换(Transformation)、连接 (Join)、聚合(Aggregation)、窗口(Window)等。DataStreamAPI集成了底层的处理函 102 Flink编程基础(Scala版) 数,使得对一些特定的操作可以提供更低层次的抽象。DataSetAPI为有界数据集提供 了额外的支持,例如循环与迭代。 (3)TableAPI以表为中心,能够动态地修改表(在表达流数据时)。TableAPI是一 种扩展的关系模型:表有二维数据结构(类似于关系数据库中的表),同时API提供可比 较的操作,例如select、project、join、group-by、aggregate等。TableAPI程序定义的是应 该执行什么样的逻辑操作,而不是直接准确地指定程序代码运行的具体步骤。尽管 TableAPI可以通过各种各样的用户自定义函数(UDF)进行扩展,但是它在表达能力上 仍然比不上核心API,不过,它使用起来会显得更加简洁(代码量更少)。除此之外,Table API程序在执行之前会经过内置优化器进行优化。用户可以在表与DataStream/ DataSet之间无缝切换,以允许程序将TableAPI与DataStreamAPI/DataSetAPI混合 使用。 4)Flinable (k提供的最高级接口是SQL 。这一层抽象在语法与表达能力上与TAPI类似,唯一的区别是通过SQL实现程序。SQL抽象与TableAPI交互密切,同时 SQL查询可以直接在TableAPI定义的表上执行。 3.lnk的应用程序结构 8 Fi 如图3-13所示,一个完整的Flink应用程序结构包含如下3部分。 图3-13 Flink应用程序结构 (1)数据源(Source):Flink在流处理和批处理上的数据源大致有4类,即基于本地 集合的数据源、基于文件的数据源、基于网络套接字的数据源、自定义的数据源。常见的 自定义数据源包括ApacheKafka、AmazonKinesisStreams 、RabbitMQ 、Twiter StreamingAPI 、ApacheNiFi等,当然用户也可以定义自己的数据源。 (2)数据转换(Transformation):数据转换的各种操作包括map、flatMap、filter、 keyBy、reduce、aggregation、window 、windowAl 、union、select等,可以将原始数据转换成 满足要求的数据。 (3)数据输出(Sink):数据输出是指Flink将转换计算后的数据发送的目的地。常 见的数据输出包括写入文件、打印到屏幕、写入Socket、自定义Sink等。常见的自定义 Sink有ApacheKafka、RabbitMQ 、MySQL 、Elasticsearch、ApacheCasandra、HadoopFileSystem等。 图3-14以一段简单代码为实例,演示了Flink的应用程序结构。 第3章Flink的设计与运行原理103 图3-14Fink应用程序结构实例 3.9Flink的数据一致性 对于分布式流处理系统,高吞吐、低延迟往往是最主要的需求。与此同时,数据一致 性在分布式流处理系统中也很重要,对于正确性要求较高的场景,“精确一次”一致性的实 现往往也非常重要。如何保证分布式系统有状态计算的一致性,是Flink作为一个分布 式流计算框架必须要解决的问题。Flink通过异步屏障快照机制来实现“精确一次”一致 性的保证,当任务中途崩溃或者取消之后,可以通过检查点或者保存点来进行恢复,实现 数据流的重放,让任务达到一致性的效果,同时,这种机制不会牺牲系统的性能。 3.1 有状态计算 9. 流计算分为无状态和有状态两种情况。无状态计算观察每个独立的事件,每条消息 来了以后和前后其他消息都没有关系,例如一个应用程序实时接收温度传感器的数据,当 温度超过40℃ 时就报警,这就是无状态的数据。有状态计算则会基于多个事件输出结 果,例如,计算过去1小时的平均温度,就属于有状态计算。 图3-15 给出了无状态流处理与有状态流处理的区别,输入记录由黑条表示。无状态 流处理每次只转换一条输入记录,并且仅根据最新的输入记录输出结果(白条)。有状态 流处理则需要维护所有已处理记录的状态值,并根据每条新输入的记录更新状态,因此输 出记录(灰条)反映的是综合考虑多个事件之后的结果。 3.2 数据一致性 9. 当在分布式系统中引入状态时,自然也引入了一致性问题。根据正确性级别的不同, 一致性可以分为如下3种形式。 104Flink编程基础(Scala版) 图3-15无状态流处理和有状态流处理的区别 (1)最多一次:尽可能正确,但不保证一定正确。也就是说,当故障发生时,什么都 不做,既不恢复丢失状态,也不重播丢失的数据。这就意味着,在系统发生故障以后,聚合 结果可能会出错。 (2)至少一次:在系统发生故障以后,聚合计算不会漏掉故障恢复之前窗口内的事 件,但可能会重复计算某些事件,这通常用于实时性较高但准确性要求不高的场合。该模 式意味着系统将以一种更加简单的方式对算子的状态进行快照处理,系统崩溃后恢复时, 算子的状态中有一些记录可能会被重放多次。例如,失败后恢复时,统计值将大于或等于 流中元素的真实值。 (3)精确一次:在系统发生故障后,聚合结果与假定没有发生故障情况时一致。该 模式意味着系统在进行恢复时,每条记录将在算子状态中只被重播一次。例如在一段数 据流中,不管该系统崩溃或者重启了多少次,该统计结果将总是与流中元素的真实个数一 致。这种语义加大了高吞吐和低延迟的实现难度。与“至少一次”模式相比,“精确一次” 模式整体的处理速度会相对比较慢,因为在开启“精确一次”模式后,为了保证一致性,就 会开启数据对齐,从而会影响系统的一些性能。 在流计算框架的发展史上,“至少一次”一致性曾经非常流行,第一代流处理框架(如 Storm 和Samza)刚问世时只能保证“至少一次”一致性。最先保证“精确一次”一次性的 系统(如StormTrident和SparkStreaming), 在性能和表现力这两方面付出了很大的代 价。而Flink在没有牺牲性能的前提下,实现了“精确一次”一致性。 9.异步屏障快照机制 3.3 “精确一次”模式要求作业从失败恢复后的状态以及管道中的数据流要与失败时一 致,通常这是通过定期对作业状态和数据流进行快照实现的。但是,传统的快照机制存在 两个主要问题。 (1)需要所有节点停止工作,即暂停整个计算过程,这个必然会影响数据处理效率和 时效性。 (2)需要保存所有节点操作中的状态以及所有在传输中的数据,这会消费大量的存 储空间。 为了解决上述问题,Flink采用了异步快照方式,它基于Chandy-lamport算法,制定 第3章Flink的设计与运行原理105 了应对流计算“精确一次”语义的检查点机制———异步屏障快照(AsynchronousBarrier Snapshot)机制。 异步屏障快照是一种轻量级的快照技术,能以低成本备份有向无环图(Directed AcyclicGraph,DAG)或有向有环图(DirectedCyclineGraph,DCG)计算作业的状态,这 使得计算作业可以频繁进行快照并且不会对性能产生明显影响。异步屏障快照机制的核 心思想是,通过屏障消息来标记触发快照的时间点和对应的数据,从而将数据流和快照时 间解耦,以实现异步快照操作,同时也大大降低了对管道数据的依赖(对DAG类作业甚 至完全不依赖),减小了随之而来的快照大小。 如图3-16所示,检查点屏障(简称“屏障”)是一种特殊的内部消息,用于将数据流从 时间上切分为多个窗口,每个窗口对应一系列连续的快照中的一个。屏障由JobManager 定时广播给计算任务所有的Source,并伴随数据流一起流至下游。每个屏障是属于当前 快照的数据与属于下个快照的数据的分割点。例如,如图3-16所示,第n-1个屏障之 后、第n个屏障之前的所有数据都属于第n个检查点。下游算子如果检测到屏障的存 在,就会触发快照动作,不必再关心时间无法静止的问题。 图3-16 实时数据流屏障 异步屏障快照机制中的“异步”指的是快照数据写入的异步性,也就是说,在算子检测 到屏障并触发快照之后,不会等待快照数据全部写入“状态后端”,而是一边后台写入,一 边立刻继续处理数据流,并将屏障发送到下游,这样就实现了最小化延迟。 3.本章小结 10 ApacheFlink是一个分布式处理引擎,用于对无界和有界数据流进行有状态计算。 Flink以数据并行和流水线方式执行任意流数据程序,流水线运行时系统可以执行批处 理和流处理程序。此外,Flink运行时本身也支持迭代算法的执行。 近年来,数据架构设计开始由传统数据处理架构、大数据Lambda架构向流处理架构 演变,这种转变使得Flink可以在大数据应用场景中“大显身手”。目前,Flink支持的典 型应用场景包括事件驱动型应用、数据分析应用和数据流水线应用。 经过多年的发展,Flink已经形成了完备的生态系统,它的技术栈可以满足企业多种 应用场景的开发需求,减轻了企业大数据应用系统的开发和维护负担。在未来,随着企业 实时应用场景的不断增多,Flink在大数据市场上的地位和作用将会更加凸显,发展前景 106Flink编程基础(Scala版) 值得期待。 3.11习题 1. 简述传统数据处理架构的局限性。 2. 简述大数据Lambda架构的优点和局限性。 3. 简述与传统数据处理架构和大数据Lambda架构相比,流处理架构具有哪些优点? 4. 举例说明Flink在企业中的应用场景。 5. 简述Flink核心组件栈包含哪些层次? 每个层次具体包含哪些内容? 6.Flink的JobManager和TaskManager具体有哪些功能? 7. 简述Flink编程模式的层次结构。 8. 对Spark、Flink和Storm 进行对比分析。 9. 简述数据一致性的3个级别。 10. 简述Flink的异步屏障快照机制。