admin管理员组文章数量:1026989
《基于Apache Flink的流处理》读书笔记
第1章 状态化流处理概述
传统数据处理
绝大多数企业所实现的传统架构都会将数据处理分为两类:
- 事务型处理
- 分析型处理
事务型处理
企业在日常业务运营过程中会用到各类应用,例如:客户管理管理软件、基于Web的应用等,这些应用系统通常都会设置独立的数据处理层(应用程序本身)和数据存储层(事务型数据库系统)。
这些应用通常会连接外部服务或实际用户,并持续处理诸如订单、邮件、网站点击等传入的数据。期间每处理一条事件,应用都会通过执行远程数据库系统的事务来读取或者更新状态,多个应用会共享同一个数据库系统,有时候还会访问相同的数据库或表。
分析型处理
存储于不同事务型数据库系统中的数据,可以为企业提供业务运营相关的分析见解。然而用于存储事务性数据的多个数据库系统通常都是相互隔离的,对于分析类查询,我们通常不会直接在事务型数据库上进行,而是将数据复制到一个撰文用来处理分析类查询的数据仓库为了填充数据仓库,需要将事务型数据库系统中数据拷贝过去。这个向数据仓库拷贝数据的过程被称为提取-转换-加载(Extract-Transform-Load,ETL)。
ETL的基本流程是:
- 从事务型数据库中提取数据
- 将其转换为通用表示形式(可能包含数据验证、数据归一化、编码、去重、表模式转换等工作)
- 加载到数据分析数据库中
为了保持数据仓库中的数据同步,ETL过程需要周期性的执行
状态化流处理
几乎所有的数据都是以连续事件流的形式产生的。事实上,现实世界中很难找到那种瞬间就生成完整数据集的例子。
任何一个处理事件流的应用,如果要支持跨多条记录的转换操作,都必须是有状态的,即能够存储和访问中间结果。应用收到事件后可以执行包括读写状态在内的任意计算。原则上,需要在应用中访问的状态有多种可选的存储位置,例如:程序变量、本地文件、嵌入式或外部数据库等。
有状态的流处理应用通常分为三类:
- 事件驱动型应用,通过接受事件流触发特定应用业务逻辑的有状态的流式应用,如实时推荐、异常检测等
- 数据管道型应用,以低延迟的方式从不同的外部系统获取、转换并插入数据,并在段时间内处理大批量数据的应用,提供多样化的数据源、数据汇连接器。Flink可以做到上述一切。
- 数据分析型应用,主要有周期性的批处理和持续性的流处理两类应用。
Flink快览
Apache Flink是一个集众多具有竞争力的特性于一身的第三代流处理引擎。它支持精确的流处理,能同事满足各种规模下对高吞吐和低延迟的要求,尤其是以下功能使其能在同类系统中脱颖而出:
- 同时支持事件时间和处理时间语义
- 提供精确一次的状态一致性保障
- 在每秒处理数百万条事件的同时保持毫秒级延迟
- 层次化的API
- 常见的存储系统的连接器
- 支持高可用配置
- 允许在不丢失应用状态的前提下更新作业代码,或进行跨Flink集群的作业迁移
- 提供详细、可定制的系统及应用指标(metrics)集合,用于提前定位和响应问题
- Flink同时也是一个成熟的批处理引擎(批是流的特例,即有界流)
第2章 流处理基础
Dataflow编程概览
Dataflow图
Dataflow程序描述了数据如何在不同的操作之间流动。Dataflow程序通常表示为有向图。图中顶点称为算子(逻辑Dataflow图称为算子,物理Dataflow图称为任务),表示计算;而边表示数据依赖关系。算子是Dataflow程序的基本功能单元,他们从输入获取数据,对其进行计算,然后产生数据并发往输出供后续处理。没有输入端的算子成为数据源,没有输出端的算子成为数据汇。一个Dataflow图至少有一个数据源和一个数据汇。
数据并行和任务并行
数据并行:将输入数据分组,让同一操作的多个任务并行执行在不同数据子集上。将计算负载分配到多个节点上从而允许处理大规模的数据
任务并行:让不同算子的任务(基于相同或不通的数据)并行计算,可以更好的利用集群的计算资源
数据交换
数据交换策略定义了如何将数据项分配给物理Dataflow图中的不同任务。常见有如下四种数据交换策略:
- 转发策略:在发送端任务和接收端任务之间一对一的进行数据传输。如果两端的任务运行在同一物理机器上,可以避免网络通信
- 广播策略:把每个数据项发往下游算子的全部任务
- 基于键值的策略:根据魔衣键值属性对数据分区,并保证键值相同的数据项会交由同一任务处理
- 随机策略:将数据均匀分配至算子的所有任务,以实现计算任务的负载均衡
并行流处理
数据流定义:一个可能无限的事件序列
延迟和吞吐
延迟:处理一个事件所需的时间。本质上,它是从接收事件到在输出中观察到事件处理效果的时间间隔。
吞吐:用来衡量系统处理能力(处理速率)的指标,它告诉我们系统每单位时间可以处理多少事件。如果系统持续以力不能及的高速率接收数据,那么缓冲区可能会用尽,继而导致数据丢失,这种情形同城称为被压。
延迟和吞吐并非相互独立的指标。如果事件在数据处理管道中传输时间太久,我们将难以保证高吞吐;同样,如果系统性能不足,事件很容易堆积缓冲,必须等待一段时间才能处理。
数据流上的操作
流处理引擎通常会提供一系列内置操作来实现数据流的获取、转换,以及输出。这些算子可以组合生成Dataflow处理图,从而时间流式应用所需的逻辑。常见有如下流式操作:
数据接入和数据输出
数据接入和数据输出操作允许流处理引擎和外部系统通信。
数据接入操作是从外部数据源获取原始数据并将其转换成合适后续处理的格式,该类算子称为数据源。
数据输出操作是将数据以合适外部系统使用的格式输出,该类算子称为数据汇。
转换操作
转换操作是一类”只过一次“的操作,它们会分别处理每个事件,对其应用某些转换并产生一条心的输出流。
滚动聚合
滚动聚合(如求和、求最值)会根据每个到来的事件持续更新结果。聚合操作都是有状态的,它们通过将新到来的事件合并到已有状态来生成更新后的聚合值。
窗口操作
有些操作必须收集并缓冲记录才能计算结果,例如流式join或像是求中位数的整体聚合。为了在无限数据流上高效的执行这些操作,必须对操作的数据加以限制。窗口操作会持续创建一些称为“桶”的有限事件合集,并允许我们基于这些有限集进行计算。
常见有如下几种窗口类型:
滚动窗口:将事件分配到长度固定且互不重叠的桶中。在窗口边界通过后,所有事件会发送给计算函数处理。可分为基于数量的滚动窗口和基于时间的滚动窗口。
滑动窗口:将事件分配到大小固定且允许重叠的桶中,这意味着每个事件可能会同时属于多个桶。我们指定长度和滑动间隔连定义滑动窗口。
会话窗口:将属于同一会话的事件分配到相同桶中。会话窗口根据会话间隔将事件分为不同的会话,该间隔值定义了会话在关闭前的非活动事件长度。
时间语义
处理时间
处理时间是当前流处理算子所在机器上的本地时钟时间。
时间事件
事件时间是数据流中事件实际发生的时间,它以附加在数据流中事件的时间戳为依据。这些时间戳通常在事件数据进入流处理管道之前就存在。
事件时间将处理速度和结果内容彻底解耦。基于事件时间的操作是可预测的,其结果具有确定性。无论数据流的处理速度如何、事件到达算子的顺序怎样,基于事件时间的窗口都会生成同样的结果。
使用事件时间要克服的挑战之一是如何处理延迟事件。普遍存在的无序问题也可以借此解决。
水位线
水位线是一个全局进度指标,表示我们确信不会再有延迟事件到来的某个时间点。本质上,水位线提供了一个逻辑时钟,用来通知系统当前的事件时间。当一个算子收到事件为T的水位线,就可以认为不会再收到任何时间戳小于或等于T的事件了。
状态和一致性模型
状态在数据处理中无处不在,任何一个稍复杂的计算都要用它。不难想象,支持有状态算子将面临很多实现上的挑战:
- 状态管理:系统需要高效的管理状态并保证它们不受并发更新影响
- 状态划分:把状态按照键值划分,并独立管理每一部分
- 状态恢复:最后一个也是最大的挑战在于,有状态算子需要保证状态可以恢复,并且即使出现故障也要确保结果正确。
结果保障
结果保障指的是流处理引擎内部状态的一致性。结果保障可分为如下几种:
- 至多一次:保证每个事件至多被处理一次,在故障时既不恢复丢失的状态,也不重放丢失的事件
- 至少一次:所有的事件都会被处理,但有些可能会被处理多次。为了确保至少一次语义,需要从源头或者缓冲区中重放事件。
- 精确一次:既不丢失事件,也不重复处理事件。
第3章 Apache Flink架构
系统架构
Flink是一个用于状态化并行流处理的分布式系统。Flink在已有集群基础设施和服务至上专注于它的核心功能——分布式数据流处理。Flink和很多集群管理器(如Apache Mesos、YARN及Kubernets)都能很好的集成;同时它也可以通过配置,作为独立的集群来运行。Flink没有提供分布式持久化存储,而是利用了现有的分布式文件系统(如HDFS)或对象存储(如S3)。它依赖Apache Zookeeper来完成高可用性设置中的领导选举。
搭建Flink所需组件
- JobManager:作为主进程,JobManager控制着单个应用程序的执行。换句话说,每个应用都由一个不同的JobManager掌控。JobManager接收需要执行的应用,该应用会包含一个所谓的JobGraph,JobManager将其转化为ExecutionGraph,然后从ResourceManager申请执行任务的必要资源(处理槽),然后在将ExecutionGraph中的任务分发给TaskManager来执行。在执行的过程中JobManager还要负责所有需要集中协调的操作,如创建检查点。
- ResourceManager:负责管理Flink的处理资源单元——TaskManager处理槽。当JobManager申请TaskManager处理槽时,ResourceManager会指示一个拥有空闲处理槽的TaskManager将其处理槽提供给JobManager。如果处理槽数无法满足JobManager的请求,ResourceManager可以和资源提供者通信,让它们提供额外容器来启动更多的TaskManager进程。同时,ResourceManager还负责终止空闲的TaskManager以释放计算资源。
- TaskManager:工作进程。通常在Flink搭建过程中会启动多个TaskManager,每个TaskManager提供一定数量的处理槽,处理槽的数目限制了一个TaskManager可执行的任务数。
- Dispatcher:跨多个作业运行。提供一个REST接口来让我们提交需要执行的应用。一旦某个应用提交执行,Dispatcher会启动一个JobManager并将应用转交给它。Dispatcher同时还会启动一个WebUI,用来提供有关作业执行的信息。
高可用设置
TaskManager故障
如果部分TaskManager故障,JobManager会向ResourceManager申请相应数量的处理槽。如果无法完成,JobManager将无法重启应用,直到有足够数量的可用处理槽。
JobManager故障
JobManager用于控制流式应用执行以及保存该过程中的源数据,如果JobManager进程消失,流式应用将无法继续处理数据。这就导致JobManager成为Flink应用中的一个单点失效组件。
JobManager在高可用模式下工作时,会依赖Zookeeper完成作业管理及元数据的迁移。具体步骤如下:
- JobManager将JobGraph以及全部所需元数据(例如应用的JAR文件)写入一个远程持久化存储系统中
- 将存储位置的路径地址写入ZK
- JobManager故障时,新进程从ZK获取存储位置,并从远程持久化存储系统中获取相关数据,申请处理槽,重启应用并利用最近一次检查点重置任务状态
Flink中的数据传输
在运行过程中,应用的任务会持续进行数据交换。TaskManager负责将数据从发送任务传输至接收任务。记录并非逐个发送的,而是在缓冲区中以批次形式发送,该技术是有效利用网络资源、实现高吞吐的基础。
发送端和接收端的任务运行在不同的TaskManager进程中时,数据交换需要利用操作系统的网络栈进行通信。在同一个TaskManager进程中时,数据会放在缓冲区和队列中,不涉及网络通信。
基于信用值的流量控制
通过网络连接逐条发送记录不但低效,还会导致很多额外开销。若想充分利用网络连接带宽,就要对数据进行缓冲。在流处理环境下,缓冲的一个明显缺点是会增加延迟,因为记录首先要收集到缓冲区而不会立即发送。Flink实现了一个基于信用值的流量控制机制,它的工作原理如下:接收任务会给发送任务授予一定信用值,其实就是保留一些用来接收它的数据的网络缓冲。一旦发送端收到信用通知,就会在信用值所限定的范围内尽可能多地传输缓冲数据,并会附带上积压量(已经填满准备传输的网络缓冲数目)大小。接收端使用保留的缓冲来处理收到的数据,同时依据各发送端的积压量信息来计算所有相连的发送端在下一轮的信用优先级。
任务链接
任务链接是Flink采用的一种用来降低某些情况下的本地通信开销的优化技术。任务链接的前提条件是,多个算子必须有相同的并行度且通过本地转发通道相连。
事件时间处理
时间戳
在事件时间模式下,Flink流式应用处理的所有记录都必须包含时间戳。时间戳将记录和特定的时间点关关联,这些时间点通常是记录所对应事件的发生时间。但实际上应用可以自由的选择时间戳的含义,只要保证流记录的时间戳会随着数据流的前进大致递增即可。
水位线
除了记录的时间戳,Flink基于事件时间的应用还必须提供水位线。水位线用于在事件时间应用中推断每个任务当前的事件时间。基于时间的算子会使用这个时间来触发计算并推动进度前进。
水位线本质上是一个包含时间戳信息的特殊记录。
水位线拥有两个基本特征:
- 必须单调递增。这是为了确保任务中的事件时间时钟正确前进,不会倒退
- 和记录的时间戳存在联系。一个时间戳为T的水位线表示,接下来所有记录的时间戳一定都大于T
状态管理
在Flink中,状态都是和特定的算子相关联。根据作用域的不同,状态可以分为两类:算子状态和键值分区状态。
算子状态
算子状态的作用域是某个算子任务,这意味着所有在同一个并行任务之内的记录都能访问到相同的状态。
键值分区状态
键值分区状态会按照算子输入记录所定义的键值来进行维护或访问。Flink为每个键值都维护了一个状态,该状态总是位于处理对应键值记录的算子任务上。
状态后端
为了保证快速访问状态,每个并行任务都会把状态维护在本地。至于状态具体的存储、访问和维护,则是由一个成为状态后端的可拔插组件来决定。状态后端主要负责两件事:本地状态管理和将状态以检查点的方式写入远程持久化存储中。
检查点、保存点及状态恢复
一致性检查点
Flink的故障恢复机制需要基于应用状态的一致性检查点。有状态的流式应用的一致性检查点是在所有任务处理完灯亮的原始输入后对全部任务状态进行的一个拷贝。
从一致性检查点中恢复
应用恢复需要经过3个步骤:
- 重启整个应用
- 利用最新的检查点重置任务状态
- 恢复所有任务的运行
Flink检查点算法
- Flink 的检查点算法用到了一种称为分界线(barrier)的特殊数据形式,用来把一条流上数据按照不同的检查点分开
- 分界线之前到来的数据导致的状态更改,都会被包含在当前分界线所属的检查点中;而基于分界线之后的数据导致的所有更改,就会被包含在之后的检查点中。
算法操作解析 :
-
现在是一个有两个输入流的应用程序,用并行的两个 Source 任务来读取
-
两条自然数数据流,蓝色数据流已经输出完蓝3了,黄色数据流输出完黄4了
-
在Souce端 Source1 接收到了数据蓝3 正在往下游发向一个数据蓝2 和 蓝3; Source2 接受到了数据黄4,且往下游发送数据黄4
-
偶数流已经处理完黄2 所以后面显示为2, 奇数流处理完蓝1 和 黄1 黄3 所以为5 并分别往下游发送每次聚合后的结果给Sink
-
JobManager 会向每个 source 任务发送一条带有新检查点 ID 的消息,通过这种方式来启动检查点,这个带有新检查点ID的东西为barrier,图中三角型表示,2只是ID
-
在Source端接受到barrier后,将自己此身的3 和 4 的数据,将它们的状态写入检查点,且向JobManager发送checkpoint成功的消息(状态后端在状态存入检查点之后,会返回通知给 source 任务,source 任务就会向 JobManager 确认检查点完),然后向下游分别发出一个检查点 barrier
-
可以看出在Source接受barrier时,数据流也在不断的处理,不会进行中断,
-
此时的偶数流已经处理完蓝2变成了4,但是还没处理到黄4,只是下游发送了一个次数的数据4,而奇数流已经处理完蓝3变成了8,并向下游发送了8
-
此时barrier都还未到奇数流和偶数流
-
此时蓝色流的barrier先一步抵达了偶数流,黄色的barrier还未到,但是因为数据的不中断一直处理,此时的先到的蓝色的barrier会将此时的偶数流的数据4进行缓存处理,流接着处理接下来的数据等待着黄色的barrier的到来,而黄色barrier之前的数据将会对缓存的数据相加
-
这次处理的总结:分界线对齐,barrier 向下游传递,sum 任务会等待所有输入分区的 barrier 到达,对于barrier已经到达的分区,继续到达的数据会被缓存。而barrier尚未到达的分区,数据会被正常处理
-
当蓝色的barrier和黄色的barrier(所有分区的)都到达后,进行状态保存到远程仓库,然后对JobManager发送消息,说自己的检查点保存完毕了
-
此时的偶数流和奇数流都为8
-
当收到所有输入分区的 barrier 时,任务就将其状态保存到状态后端的检查点中,然后将 barrier 继续向下游转发
-
向下游转发检查点 barrier 后,任务继续正常的数据处理
-
Sink 任务向 JobManager 确认状态保存到 checkpoint 完毕
-
当所有任务都确认已成功将状态保存到检查点时,检查点就真正完成了
保存点
原则上,保存点的生成算法和检查点完全一样,因此可以把保存点看做包含一些额外元数据的检查点。保存点的生成不是由Flink自动完成,而是需要由用户(外部调度器)显式触发。同时,Flink也不会自动清理保存点。
第4章 Apache Flink开发环境
主要介绍如何搭建一个用于开发、运行和调试Flink的应用环境。
略~
第5章 DataStream API
基本流程
- 设置执行环境:决定应用是在本地机器还是在集群上运行
- 读取输入流:数据流的来源可以是消息队列或文件,也可以是实时生成的
- 应用转换:对输入流进行处理,应用转换,如使用keyBy()转换,将输入流按照指定信息(ID等)进行分区
- 输出结果:将结果发送到某些外部系统
- 执行:完成上述应用定义后即可执行应用。Flink程序都是通过延迟计算的方式执行。只有在调用execute()方法时,系统才会触发程序执行。
本章主要介绍Flink DataStream API的基础知识,具体用法可参见配套示例
第6章 基于时间和窗口的算子
时间特性分类
处理时间ProcessingTime
指定算子根据处理机器的系统时钟决定数据流当时的时间。处理时间窗口基于机器时间触发,通常情况下会导致不确定的结果发生,这是因为窗口内容取决于元素到达的速率。在该配置下,由于无需依赖水位线来驱动事件时间的前进,可以提供极低的延迟。
事件时间EventTime
指定算子根据数据自身包含的信息决定当前时间。每个事件时间都带有时间戳,而系统的逻辑时间是由水位线来定义。在该配置下,即使事件乱序到达,事件时间窗口也会计算出确定的结果。窗口结果不会取决于数据流的读取或者处理速度。
摄入时间IngestionTime
指定每个接受的记录都把在数据源算子的处理时间作为事件时间的时间戳,并自动生成水位线。摄入时间是处理时间和事件时间的混合体,它表示事件进入流处理引擎的时间。
本章主要介绍Flink DataStream API用于处理时间的方法和基于时间的算子,具体用法可参见配套示例
第7章 有状态算子和应用
本章主要介绍如何实现有状态的用户自定义函数,如如何实现带有键值分区状态及算子状态的函数。
略~
第8章 读写外部系统
应用的一致性保障
Flink的检查点和恢复机制会周期性的为应用黄台创建一致性检查点。一旦发生故障,应用会从最近一次完成的检查点中恢复状态并继续处理数据。虽然如此,但像这样把应用状态重置到某个一致性检查点所提供的应用处理保障还无法令人满意。我们需要应用的数据源和数据汇连接器能和Flink的检查点及恢复策略集成,并提供某些特定的属性以及支持各类有意义的保障。
为了在应用中实现精确一次的的状态一致性保障,应用的每个数据源连接器都需要支持将数据读取位置重置为某个已有检查点中的值。如果应用使用的数据源连接器无法存储和重置读取位置,那么在它出现故障时就可能要丢失部分数据,从而只能提供最多一次的保障。
Flink的检查点和恢复机制结合可重置的数据源连接器能够确保应用不会丢失数据。但由于在前一次成功的检查点后发出的数据会被再次发送,所以应用可能会发出两次结果。因此,可重置的数据源以及Flink的恢复机制虽然可以为应用状态提供精确一次的一致性保障,但无法提供端到端的精确一次保障。
应用若现提供端到端的精确一次保障,需要一些特殊的数据汇连接器。根据情况的不通个,这些连接器可以使用两张技术来实现精确一次保障:幂等性写和事务型写。
幂等性写
幂等操作可以多次执行,但只会引起一次改变。幂等性写操作对于流式应用而言具有重要意义,因为它们可以在不改变结果的前提下多次执行。因此,幂等性写操作可以再一定程度上减轻Flink检查点机制所带来的重复结果的影响。
事务性写
实现端到端精确一次一致性的第二个途径是事务性写。它的基本思路是只有在上次成功的检查点之前计算的结果才会被写入外部数据汇系统。该行为可以提供端到端的精确一次保障。因为在发生故障后,应用会被重置到上一个检查点,而接受系统不会收到任何在该检查点之后生成的结果。
事务性写不会出现重放过程中的不一致现象,但会增加一定延迟,因为结果只有在检查点完成之后才对外可见。
Flink提供了两个构建来实现事务性的数据汇链接器:一个通用的WAL数据汇和一个2PC数据汇。
WAL数据汇会将所有结果记录写入应用状态,并在收到检查点完成通知后将他们发送到数据汇系统。由于该数据会利用状态后端缓冲记录,所以它使用于任何数据汇系统。然而,事务性写会导致应用状态大小增加以及接收系统需要处理一次次的波峰式写入。
与WAL不同的是,2PC数据汇需要数据汇系统提供事务支持,或者提供模拟事务的支持。对于每个检查点,数据汇首先启动一个事务,将所有接收到的记录添加到事务中,并将它们写入数据汇系统,但是不提交。当它收到一个“检查点完成”的通知后,它提交事务,并将结果落盘。
2PC协议集成在Flink的检查点机制中。检查点分隔符便是启动一个新事务的通知,所有算子中对于它“自身检查点完成”的通知,即是它们的提交投票。JobManager的对于“整个检查点完成”的消息,即为提交事务的指示。
相对于WAL数据汇,2PC数据汇是基于数据汇系统以及数据汇的实现方式,达到精确一次的输出保障。而相对于WAL数据汇的突增写入模式,2PC数据汇为持续向sink 系统写入记录。
不同数据源和数据汇组合所能实现的端到端的一致性保障:
不可重置数据源 | 可重置数据源 | |
---|---|---|
任意数据汇 | 至多一次 | 至少一次 |
幂等性数据汇 | 至多一次 | 精确一次(故障恢复过程中会有临时性不一致) |
WAL数据汇 | 至多一次 | 至少一次 |
2PC数据汇 | 至多一次 | 精确一次 |
内置连接器
Flink为很多外部存储系统都提供了相应的数据读写连接器。消息队列是一类常见的数据流消息来源。在以批处理为主的环境中,我们还经常通过监视文件系统目录并读取其中新增文件的方式来获取数据流。
在数据汇一段,数据流中的事件经常会写入消息队列中,以支撑后续流式应用;或者是写入文件系统,实现归档或支撑后续离线分析及批处理应用;也可以插入到键值存储或数据库系统中,以供查询、搜索或仪表盘应用使用。
Flink为Apache Kafka、Kinesis、RabbitMQ、Apache Cassandra、ElasticSearch、多种文件系统以及JDBC等都提供了相应的连接器。
除了内置的连接器,Flink也支持自定义数据源和数据汇链接器。
- 实现SourceFunction接口和RichSourceFunction抽象类可以自定义非并行的数据源连接器,即只能以单任务云心
- 实现ParallelSourceFunction接口和RichParallelSourceFunction抽象类可用于定义能够同时运行多个任务实例的数据源连接器
- 实现SinkFunction接口和RichSinkFunction抽象类可用于定义数据汇连接器。
异步访问外部系统
除了淡出的手法数据之外,我们还经常需要利用从远程数据库获取的信息来丰富数据流,此时也会涉及和外部存储系统的交互。
Flink提供的AsyncFunction可以有效降低I/O调用所带来的延迟。该函数能够同时发出多个查询并对其结果进行异步处理。它可以通过配置选择对记录进行保存,也可以为了追求更低的延迟按照请求结果的返回顺序处理记录。
为了充分利用AsyncFunction,外部系统最好能够提供一个支持异步调用的客户端,很多现有系统都可以做到这点。而如果外部系统只提供了同步客户端,你可以通过多线程的方式来发送请求并对其进行处理。
第9章 搭建Flink运行流式应用
本章主要介绍Flink集群的多种部署方式以及如何对它进行安全和高可用配置。可参见搭建Flink运行流式应用。略~
第10章 Flink和流式应用运维
运行并管理流式应用
保存点
保存点和检查点的本质相同,二者都是应用状态的一致性完整快照。但他们的声明周期有所差异。检查点会自动创建,在发生故障时自动加载并由Flink自动删除(取决于应用具体配置)。此外,除非应用显式指定要保留检查点,否则它们会在应用取消时自动删除。而保存点则与之相反,它们需要由用户或外部服务手动触发,且永远不会被Flink自动删除。
每个保存点都对应一个持久化数据存储上的目录。它由一个包含了所有任务状态数据文件的子目录和一个包含了全部数据文件绝对路径的二进制元数据文件组成。
通过命令行客户端管理应用
Flink命令行客户端提供了启动、停止和管理Flink应用的功能。它会从./conf/flink-conf.ymal
文件中读取配置。
通过REST API管理应用
REST API可供用户或脚本直接访问,它可以对外公开有关Flink集群和应用的信息,包括指标数据及用于提交和控制应用程序的服务端点等。Flink使用一个Web服务器来同事支持REST API和Web UI,该服务会作为Dispatcher进程的一部分来运行。
控制任务进度
为了实现并行执行,Flink应用会将算子划分为不同的任务,并将这些任务分配到集群中的不同工作进程上。任务分配的目标工作进程,任务的共存情况以及工作进程中的任务数都会对应用性能产生显著影响。
控制任务链接
任务链接指的是将两个或多个算子的并行任务融合在一起,从而可以从它们在同一线程中执行。融合的任务只需通过方法调用就可以进行记录交换,因此几乎没有通信成本。由于任务链接可以提高大多数应用的性能,所以Flink默认会启用它。
然而,也有特定的应用可能无法从中受益。其中一种情况是我们希望将一连串负载较重的函数拆开,让它们在不通的处理槽内执行。Flink支持禁用应用内的任务链接,也可以控制单个算子的链接行为。
定义处理共享槽
Flink默认任务调度策略会将一个完整的程序分片分配到一个处理槽中。根据应用的复杂度以及算子的计算成本,Flink提供了处理槽共享组机制,允许用户手动将任务分配到处理槽中。
调整检查点及恢复
在启用容错功能的情况下,Flink会周期性的将应用状态存储到检查点中。由于在生成检查点时可能需要将大量数据写入持久化存储中,所以其代价可能非常昂贵。增大检查点的生成间隔可以降低常规处理过程中的容错开销,但它同时会使作业在故障恢复过程中需要重新处理更多的数据。
配置检查点
Flink提供了一系列用于调整检查点和状态后端的参数。包括但不限于:指定生成间隔、启用检查点压缩、应用停止后保留检查点等。
配置状态后端
应用状态后端负责维护本地状态,生成检查点和保存点以及在故障时恢复应用状态。因此,应用状态后端的选择和配置对检查点相关操作的性能有很大影响。
Flink默认状态后端是MemorStateBackend。由于它将所有状态保存在内存中,而且检查点全部位于易失且受JVM大小约束的JobManager堆存储内,所以不建议将其用于生产环境。
Flink还支持配置FsStateBackend和RocksDBStateBackend状态后端。
配置故障恢复
当一个拥有检查点的应用发生故障时,它会经过一系列步骤重启,具体包括启动任务、恢复状态(包括数据源任务的读取偏移)和继续处理。为了能够赶得上数据流的进度,应用处理积累数据的速率必须要高于新数据到来的速率。它在追赶进度期间的处理延迟会有所增加。因此,从重启到成功恢复常规处理的进度追赶期间,应用需要足够多的备用资源。
恢复过程除了资源因素,还有两个两个值得关注的主题:重启策略和本地恢复。
重启策略
Flink提供了三种重启策略:
- fixed-delay:已配置的固定时间间隔重试将应用重启某个固定的次数
- failture-rate:允许在未超过故障率的前提下不断重启应用。故障率的定义为某个时间间隔内的最大故障次数
- no-restart:不重启应用,直接失败
本地恢复
Flink支持一种称为本地恢复的特性,能够在应用从相同机器重启时显著提高恢复速度。在启用该功能后状态后端除了将数据写入远程存储系统外,还会将检查点数据在工作进程所在的节点的本地磁盘复制一份。当应用需要重启时,Flink会尝试将相同的任务调度到和之前相同的工作节点执行。如果成功,则任务会优先尝试从本地磁盘加载检查点数据。如果出现任何问题,则将退回到使用远程存储进行处理。
监控Flink集群和应用
Flink Web UI
Flink提供的了解集群和内部作业情况概要最为简单的方式。可以通过http://<jobmanager-hostname>:8081
地址来访问它。
详细分析可参见Flink Web UI分析
指标系统
Flink在默认情况下会收集很多系统和应用指标。指标的手机是按照每个算子、每个TaskManager或JobManager来进行的。
配置日志行为
日志是调试和理解应用行为的另一个重要工具。默认情况下,Flink使用SLF4J日志抽象和log4j日志框架。要修改log4j记录器的属性,可以通过修改conf/目录中的log4j.properties文件来实现。
第11章还有什么?
Flink生态的其他组成部分
用于批处理的DataSet API
Flink可用于实现有界数据的一次性或定期查询。DataSet程序和DataStream程序一样,都是有一系列转换操作组成。二者的不同在于前者是一个有界数据集。
用于关系型分析的Table API及SQL
虽然Flink底层DataStream和DataSet的API是分开的,但你可以使用高层次的关系型API——Table API和SQL,实现流批一体的分析。
用于复杂事件处理和模式匹配的FlinkCEP
FlinkCEP是一个用于复杂事件模式检测的高层次API库。它基于DataStream API实现,允许你指定期望在数据流中检测到的模式。常见的CEP应用场景包括金融应用,欺诈检测,复杂系统中的监控和报警,以及检测网络入侵。
用于图计算的Gelly
Gelly是Flink的图计算API库。它建立在DataSet API和Flink的高效批量迭代之上。它包含了一组常见的图算法,方便日常使用。
《基于Apache Flink的流处理》读书笔记
第1章 状态化流处理概述
传统数据处理
绝大多数企业所实现的传统架构都会将数据处理分为两类:
- 事务型处理
- 分析型处理
事务型处理
企业在日常业务运营过程中会用到各类应用,例如:客户管理管理软件、基于Web的应用等,这些应用系统通常都会设置独立的数据处理层(应用程序本身)和数据存储层(事务型数据库系统)。
这些应用通常会连接外部服务或实际用户,并持续处理诸如订单、邮件、网站点击等传入的数据。期间每处理一条事件,应用都会通过执行远程数据库系统的事务来读取或者更新状态,多个应用会共享同一个数据库系统,有时候还会访问相同的数据库或表。
分析型处理
存储于不同事务型数据库系统中的数据,可以为企业提供业务运营相关的分析见解。然而用于存储事务性数据的多个数据库系统通常都是相互隔离的,对于分析类查询,我们通常不会直接在事务型数据库上进行,而是将数据复制到一个撰文用来处理分析类查询的数据仓库为了填充数据仓库,需要将事务型数据库系统中数据拷贝过去。这个向数据仓库拷贝数据的过程被称为提取-转换-加载(Extract-Transform-Load,ETL)。
ETL的基本流程是:
- 从事务型数据库中提取数据
- 将其转换为通用表示形式(可能包含数据验证、数据归一化、编码、去重、表模式转换等工作)
- 加载到数据分析数据库中
为了保持数据仓库中的数据同步,ETL过程需要周期性的执行
状态化流处理
几乎所有的数据都是以连续事件流的形式产生的。事实上,现实世界中很难找到那种瞬间就生成完整数据集的例子。
任何一个处理事件流的应用,如果要支持跨多条记录的转换操作,都必须是有状态的,即能够存储和访问中间结果。应用收到事件后可以执行包括读写状态在内的任意计算。原则上,需要在应用中访问的状态有多种可选的存储位置,例如:程序变量、本地文件、嵌入式或外部数据库等。
有状态的流处理应用通常分为三类:
- 事件驱动型应用,通过接受事件流触发特定应用业务逻辑的有状态的流式应用,如实时推荐、异常检测等
- 数据管道型应用,以低延迟的方式从不同的外部系统获取、转换并插入数据,并在段时间内处理大批量数据的应用,提供多样化的数据源、数据汇连接器。Flink可以做到上述一切。
- 数据分析型应用,主要有周期性的批处理和持续性的流处理两类应用。
Flink快览
Apache Flink是一个集众多具有竞争力的特性于一身的第三代流处理引擎。它支持精确的流处理,能同事满足各种规模下对高吞吐和低延迟的要求,尤其是以下功能使其能在同类系统中脱颖而出:
- 同时支持事件时间和处理时间语义
- 提供精确一次的状态一致性保障
- 在每秒处理数百万条事件的同时保持毫秒级延迟
- 层次化的API
- 常见的存储系统的连接器
- 支持高可用配置
- 允许在不丢失应用状态的前提下更新作业代码,或进行跨Flink集群的作业迁移
- 提供详细、可定制的系统及应用指标(metrics)集合,用于提前定位和响应问题
- Flink同时也是一个成熟的批处理引擎(批是流的特例,即有界流)
第2章 流处理基础
Dataflow编程概览
Dataflow图
Dataflow程序描述了数据如何在不同的操作之间流动。Dataflow程序通常表示为有向图。图中顶点称为算子(逻辑Dataflow图称为算子,物理Dataflow图称为任务),表示计算;而边表示数据依赖关系。算子是Dataflow程序的基本功能单元,他们从输入获取数据,对其进行计算,然后产生数据并发往输出供后续处理。没有输入端的算子成为数据源,没有输出端的算子成为数据汇。一个Dataflow图至少有一个数据源和一个数据汇。
数据并行和任务并行
数据并行:将输入数据分组,让同一操作的多个任务并行执行在不同数据子集上。将计算负载分配到多个节点上从而允许处理大规模的数据
任务并行:让不同算子的任务(基于相同或不通的数据)并行计算,可以更好的利用集群的计算资源
数据交换
数据交换策略定义了如何将数据项分配给物理Dataflow图中的不同任务。常见有如下四种数据交换策略:
- 转发策略:在发送端任务和接收端任务之间一对一的进行数据传输。如果两端的任务运行在同一物理机器上,可以避免网络通信
- 广播策略:把每个数据项发往下游算子的全部任务
- 基于键值的策略:根据魔衣键值属性对数据分区,并保证键值相同的数据项会交由同一任务处理
- 随机策略:将数据均匀分配至算子的所有任务,以实现计算任务的负载均衡
并行流处理
数据流定义:一个可能无限的事件序列
延迟和吞吐
延迟:处理一个事件所需的时间。本质上,它是从接收事件到在输出中观察到事件处理效果的时间间隔。
吞吐:用来衡量系统处理能力(处理速率)的指标,它告诉我们系统每单位时间可以处理多少事件。如果系统持续以力不能及的高速率接收数据,那么缓冲区可能会用尽,继而导致数据丢失,这种情形同城称为被压。
延迟和吞吐并非相互独立的指标。如果事件在数据处理管道中传输时间太久,我们将难以保证高吞吐;同样,如果系统性能不足,事件很容易堆积缓冲,必须等待一段时间才能处理。
数据流上的操作
流处理引擎通常会提供一系列内置操作来实现数据流的获取、转换,以及输出。这些算子可以组合生成Dataflow处理图,从而时间流式应用所需的逻辑。常见有如下流式操作:
数据接入和数据输出
数据接入和数据输出操作允许流处理引擎和外部系统通信。
数据接入操作是从外部数据源获取原始数据并将其转换成合适后续处理的格式,该类算子称为数据源。
数据输出操作是将数据以合适外部系统使用的格式输出,该类算子称为数据汇。
转换操作
转换操作是一类”只过一次“的操作,它们会分别处理每个事件,对其应用某些转换并产生一条心的输出流。
滚动聚合
滚动聚合(如求和、求最值)会根据每个到来的事件持续更新结果。聚合操作都是有状态的,它们通过将新到来的事件合并到已有状态来生成更新后的聚合值。
窗口操作
有些操作必须收集并缓冲记录才能计算结果,例如流式join或像是求中位数的整体聚合。为了在无限数据流上高效的执行这些操作,必须对操作的数据加以限制。窗口操作会持续创建一些称为“桶”的有限事件合集,并允许我们基于这些有限集进行计算。
常见有如下几种窗口类型:
滚动窗口:将事件分配到长度固定且互不重叠的桶中。在窗口边界通过后,所有事件会发送给计算函数处理。可分为基于数量的滚动窗口和基于时间的滚动窗口。
滑动窗口:将事件分配到大小固定且允许重叠的桶中,这意味着每个事件可能会同时属于多个桶。我们指定长度和滑动间隔连定义滑动窗口。
会话窗口:将属于同一会话的事件分配到相同桶中。会话窗口根据会话间隔将事件分为不同的会话,该间隔值定义了会话在关闭前的非活动事件长度。
时间语义
处理时间
处理时间是当前流处理算子所在机器上的本地时钟时间。
时间事件
事件时间是数据流中事件实际发生的时间,它以附加在数据流中事件的时间戳为依据。这些时间戳通常在事件数据进入流处理管道之前就存在。
事件时间将处理速度和结果内容彻底解耦。基于事件时间的操作是可预测的,其结果具有确定性。无论数据流的处理速度如何、事件到达算子的顺序怎样,基于事件时间的窗口都会生成同样的结果。
使用事件时间要克服的挑战之一是如何处理延迟事件。普遍存在的无序问题也可以借此解决。
水位线
水位线是一个全局进度指标,表示我们确信不会再有延迟事件到来的某个时间点。本质上,水位线提供了一个逻辑时钟,用来通知系统当前的事件时间。当一个算子收到事件为T的水位线,就可以认为不会再收到任何时间戳小于或等于T的事件了。
状态和一致性模型
状态在数据处理中无处不在,任何一个稍复杂的计算都要用它。不难想象,支持有状态算子将面临很多实现上的挑战:
- 状态管理:系统需要高效的管理状态并保证它们不受并发更新影响
- 状态划分:把状态按照键值划分,并独立管理每一部分
- 状态恢复:最后一个也是最大的挑战在于,有状态算子需要保证状态可以恢复,并且即使出现故障也要确保结果正确。
结果保障
结果保障指的是流处理引擎内部状态的一致性。结果保障可分为如下几种:
- 至多一次:保证每个事件至多被处理一次,在故障时既不恢复丢失的状态,也不重放丢失的事件
- 至少一次:所有的事件都会被处理,但有些可能会被处理多次。为了确保至少一次语义,需要从源头或者缓冲区中重放事件。
- 精确一次:既不丢失事件,也不重复处理事件。
第3章 Apache Flink架构
系统架构
Flink是一个用于状态化并行流处理的分布式系统。Flink在已有集群基础设施和服务至上专注于它的核心功能——分布式数据流处理。Flink和很多集群管理器(如Apache Mesos、YARN及Kubernets)都能很好的集成;同时它也可以通过配置,作为独立的集群来运行。Flink没有提供分布式持久化存储,而是利用了现有的分布式文件系统(如HDFS)或对象存储(如S3)。它依赖Apache Zookeeper来完成高可用性设置中的领导选举。
搭建Flink所需组件
- JobManager:作为主进程,JobManager控制着单个应用程序的执行。换句话说,每个应用都由一个不同的JobManager掌控。JobManager接收需要执行的应用,该应用会包含一个所谓的JobGraph,JobManager将其转化为ExecutionGraph,然后从ResourceManager申请执行任务的必要资源(处理槽),然后在将ExecutionGraph中的任务分发给TaskManager来执行。在执行的过程中JobManager还要负责所有需要集中协调的操作,如创建检查点。
- ResourceManager:负责管理Flink的处理资源单元——TaskManager处理槽。当JobManager申请TaskManager处理槽时,ResourceManager会指示一个拥有空闲处理槽的TaskManager将其处理槽提供给JobManager。如果处理槽数无法满足JobManager的请求,ResourceManager可以和资源提供者通信,让它们提供额外容器来启动更多的TaskManager进程。同时,ResourceManager还负责终止空闲的TaskManager以释放计算资源。
- TaskManager:工作进程。通常在Flink搭建过程中会启动多个TaskManager,每个TaskManager提供一定数量的处理槽,处理槽的数目限制了一个TaskManager可执行的任务数。
- Dispatcher:跨多个作业运行。提供一个REST接口来让我们提交需要执行的应用。一旦某个应用提交执行,Dispatcher会启动一个JobManager并将应用转交给它。Dispatcher同时还会启动一个WebUI,用来提供有关作业执行的信息。
高可用设置
TaskManager故障
如果部分TaskManager故障,JobManager会向ResourceManager申请相应数量的处理槽。如果无法完成,JobManager将无法重启应用,直到有足够数量的可用处理槽。
JobManager故障
JobManager用于控制流式应用执行以及保存该过程中的源数据,如果JobManager进程消失,流式应用将无法继续处理数据。这就导致JobManager成为Flink应用中的一个单点失效组件。
JobManager在高可用模式下工作时,会依赖Zookeeper完成作业管理及元数据的迁移。具体步骤如下:
- JobManager将JobGraph以及全部所需元数据(例如应用的JAR文件)写入一个远程持久化存储系统中
- 将存储位置的路径地址写入ZK
- JobManager故障时,新进程从ZK获取存储位置,并从远程持久化存储系统中获取相关数据,申请处理槽,重启应用并利用最近一次检查点重置任务状态
Flink中的数据传输
在运行过程中,应用的任务会持续进行数据交换。TaskManager负责将数据从发送任务传输至接收任务。记录并非逐个发送的,而是在缓冲区中以批次形式发送,该技术是有效利用网络资源、实现高吞吐的基础。
发送端和接收端的任务运行在不同的TaskManager进程中时,数据交换需要利用操作系统的网络栈进行通信。在同一个TaskManager进程中时,数据会放在缓冲区和队列中,不涉及网络通信。
基于信用值的流量控制
通过网络连接逐条发送记录不但低效,还会导致很多额外开销。若想充分利用网络连接带宽,就要对数据进行缓冲。在流处理环境下,缓冲的一个明显缺点是会增加延迟,因为记录首先要收集到缓冲区而不会立即发送。Flink实现了一个基于信用值的流量控制机制,它的工作原理如下:接收任务会给发送任务授予一定信用值,其实就是保留一些用来接收它的数据的网络缓冲。一旦发送端收到信用通知,就会在信用值所限定的范围内尽可能多地传输缓冲数据,并会附带上积压量(已经填满准备传输的网络缓冲数目)大小。接收端使用保留的缓冲来处理收到的数据,同时依据各发送端的积压量信息来计算所有相连的发送端在下一轮的信用优先级。
任务链接
任务链接是Flink采用的一种用来降低某些情况下的本地通信开销的优化技术。任务链接的前提条件是,多个算子必须有相同的并行度且通过本地转发通道相连。
事件时间处理
时间戳
在事件时间模式下,Flink流式应用处理的所有记录都必须包含时间戳。时间戳将记录和特定的时间点关关联,这些时间点通常是记录所对应事件的发生时间。但实际上应用可以自由的选择时间戳的含义,只要保证流记录的时间戳会随着数据流的前进大致递增即可。
水位线
除了记录的时间戳,Flink基于事件时间的应用还必须提供水位线。水位线用于在事件时间应用中推断每个任务当前的事件时间。基于时间的算子会使用这个时间来触发计算并推动进度前进。
水位线本质上是一个包含时间戳信息的特殊记录。
水位线拥有两个基本特征:
- 必须单调递增。这是为了确保任务中的事件时间时钟正确前进,不会倒退
- 和记录的时间戳存在联系。一个时间戳为T的水位线表示,接下来所有记录的时间戳一定都大于T
状态管理
在Flink中,状态都是和特定的算子相关联。根据作用域的不同,状态可以分为两类:算子状态和键值分区状态。
算子状态
算子状态的作用域是某个算子任务,这意味着所有在同一个并行任务之内的记录都能访问到相同的状态。
键值分区状态
键值分区状态会按照算子输入记录所定义的键值来进行维护或访问。Flink为每个键值都维护了一个状态,该状态总是位于处理对应键值记录的算子任务上。
状态后端
为了保证快速访问状态,每个并行任务都会把状态维护在本地。至于状态具体的存储、访问和维护,则是由一个成为状态后端的可拔插组件来决定。状态后端主要负责两件事:本地状态管理和将状态以检查点的方式写入远程持久化存储中。
检查点、保存点及状态恢复
一致性检查点
Flink的故障恢复机制需要基于应用状态的一致性检查点。有状态的流式应用的一致性检查点是在所有任务处理完灯亮的原始输入后对全部任务状态进行的一个拷贝。
从一致性检查点中恢复
应用恢复需要经过3个步骤:
- 重启整个应用
- 利用最新的检查点重置任务状态
- 恢复所有任务的运行
Flink检查点算法
- Flink 的检查点算法用到了一种称为分界线(barrier)的特殊数据形式,用来把一条流上数据按照不同的检查点分开
- 分界线之前到来的数据导致的状态更改,都会被包含在当前分界线所属的检查点中;而基于分界线之后的数据导致的所有更改,就会被包含在之后的检查点中。
算法操作解析 :
-
现在是一个有两个输入流的应用程序,用并行的两个 Source 任务来读取
-
两条自然数数据流,蓝色数据流已经输出完蓝3了,黄色数据流输出完黄4了
-
在Souce端 Source1 接收到了数据蓝3 正在往下游发向一个数据蓝2 和 蓝3; Source2 接受到了数据黄4,且往下游发送数据黄4
-
偶数流已经处理完黄2 所以后面显示为2, 奇数流处理完蓝1 和 黄1 黄3 所以为5 并分别往下游发送每次聚合后的结果给Sink
-
JobManager 会向每个 source 任务发送一条带有新检查点 ID 的消息,通过这种方式来启动检查点,这个带有新检查点ID的东西为barrier,图中三角型表示,2只是ID
-
在Source端接受到barrier后,将自己此身的3 和 4 的数据,将它们的状态写入检查点,且向JobManager发送checkpoint成功的消息(状态后端在状态存入检查点之后,会返回通知给 source 任务,source 任务就会向 JobManager 确认检查点完),然后向下游分别发出一个检查点 barrier
-
可以看出在Source接受barrier时,数据流也在不断的处理,不会进行中断,
-
此时的偶数流已经处理完蓝2变成了4,但是还没处理到黄4,只是下游发送了一个次数的数据4,而奇数流已经处理完蓝3变成了8,并向下游发送了8
-
此时barrier都还未到奇数流和偶数流
-
此时蓝色流的barrier先一步抵达了偶数流,黄色的barrier还未到,但是因为数据的不中断一直处理,此时的先到的蓝色的barrier会将此时的偶数流的数据4进行缓存处理,流接着处理接下来的数据等待着黄色的barrier的到来,而黄色barrier之前的数据将会对缓存的数据相加
-
这次处理的总结:分界线对齐,barrier 向下游传递,sum 任务会等待所有输入分区的 barrier 到达,对于barrier已经到达的分区,继续到达的数据会被缓存。而barrier尚未到达的分区,数据会被正常处理
-
当蓝色的barrier和黄色的barrier(所有分区的)都到达后,进行状态保存到远程仓库,然后对JobManager发送消息,说自己的检查点保存完毕了
-
此时的偶数流和奇数流都为8
-
当收到所有输入分区的 barrier 时,任务就将其状态保存到状态后端的检查点中,然后将 barrier 继续向下游转发
-
向下游转发检查点 barrier 后,任务继续正常的数据处理
-
Sink 任务向 JobManager 确认状态保存到 checkpoint 完毕
-
当所有任务都确认已成功将状态保存到检查点时,检查点就真正完成了
保存点
原则上,保存点的生成算法和检查点完全一样,因此可以把保存点看做包含一些额外元数据的检查点。保存点的生成不是由Flink自动完成,而是需要由用户(外部调度器)显式触发。同时,Flink也不会自动清理保存点。
第4章 Apache Flink开发环境
主要介绍如何搭建一个用于开发、运行和调试Flink的应用环境。
略~
第5章 DataStream API
基本流程
- 设置执行环境:决定应用是在本地机器还是在集群上运行
- 读取输入流:数据流的来源可以是消息队列或文件,也可以是实时生成的
- 应用转换:对输入流进行处理,应用转换,如使用keyBy()转换,将输入流按照指定信息(ID等)进行分区
- 输出结果:将结果发送到某些外部系统
- 执行:完成上述应用定义后即可执行应用。Flink程序都是通过延迟计算的方式执行。只有在调用execute()方法时,系统才会触发程序执行。
本章主要介绍Flink DataStream API的基础知识,具体用法可参见配套示例
第6章 基于时间和窗口的算子
时间特性分类
处理时间ProcessingTime
指定算子根据处理机器的系统时钟决定数据流当时的时间。处理时间窗口基于机器时间触发,通常情况下会导致不确定的结果发生,这是因为窗口内容取决于元素到达的速率。在该配置下,由于无需依赖水位线来驱动事件时间的前进,可以提供极低的延迟。
事件时间EventTime
指定算子根据数据自身包含的信息决定当前时间。每个事件时间都带有时间戳,而系统的逻辑时间是由水位线来定义。在该配置下,即使事件乱序到达,事件时间窗口也会计算出确定的结果。窗口结果不会取决于数据流的读取或者处理速度。
摄入时间IngestionTime
指定每个接受的记录都把在数据源算子的处理时间作为事件时间的时间戳,并自动生成水位线。摄入时间是处理时间和事件时间的混合体,它表示事件进入流处理引擎的时间。
本章主要介绍Flink DataStream API用于处理时间的方法和基于时间的算子,具体用法可参见配套示例
第7章 有状态算子和应用
本章主要介绍如何实现有状态的用户自定义函数,如如何实现带有键值分区状态及算子状态的函数。
略~
第8章 读写外部系统
应用的一致性保障
Flink的检查点和恢复机制会周期性的为应用黄台创建一致性检查点。一旦发生故障,应用会从最近一次完成的检查点中恢复状态并继续处理数据。虽然如此,但像这样把应用状态重置到某个一致性检查点所提供的应用处理保障还无法令人满意。我们需要应用的数据源和数据汇连接器能和Flink的检查点及恢复策略集成,并提供某些特定的属性以及支持各类有意义的保障。
为了在应用中实现精确一次的的状态一致性保障,应用的每个数据源连接器都需要支持将数据读取位置重置为某个已有检查点中的值。如果应用使用的数据源连接器无法存储和重置读取位置,那么在它出现故障时就可能要丢失部分数据,从而只能提供最多一次的保障。
Flink的检查点和恢复机制结合可重置的数据源连接器能够确保应用不会丢失数据。但由于在前一次成功的检查点后发出的数据会被再次发送,所以应用可能会发出两次结果。因此,可重置的数据源以及Flink的恢复机制虽然可以为应用状态提供精确一次的一致性保障,但无法提供端到端的精确一次保障。
应用若现提供端到端的精确一次保障,需要一些特殊的数据汇连接器。根据情况的不通个,这些连接器可以使用两张技术来实现精确一次保障:幂等性写和事务型写。
幂等性写
幂等操作可以多次执行,但只会引起一次改变。幂等性写操作对于流式应用而言具有重要意义,因为它们可以在不改变结果的前提下多次执行。因此,幂等性写操作可以再一定程度上减轻Flink检查点机制所带来的重复结果的影响。
事务性写
实现端到端精确一次一致性的第二个途径是事务性写。它的基本思路是只有在上次成功的检查点之前计算的结果才会被写入外部数据汇系统。该行为可以提供端到端的精确一次保障。因为在发生故障后,应用会被重置到上一个检查点,而接受系统不会收到任何在该检查点之后生成的结果。
事务性写不会出现重放过程中的不一致现象,但会增加一定延迟,因为结果只有在检查点完成之后才对外可见。
Flink提供了两个构建来实现事务性的数据汇链接器:一个通用的WAL数据汇和一个2PC数据汇。
WAL数据汇会将所有结果记录写入应用状态,并在收到检查点完成通知后将他们发送到数据汇系统。由于该数据会利用状态后端缓冲记录,所以它使用于任何数据汇系统。然而,事务性写会导致应用状态大小增加以及接收系统需要处理一次次的波峰式写入。
与WAL不同的是,2PC数据汇需要数据汇系统提供事务支持,或者提供模拟事务的支持。对于每个检查点,数据汇首先启动一个事务,将所有接收到的记录添加到事务中,并将它们写入数据汇系统,但是不提交。当它收到一个“检查点完成”的通知后,它提交事务,并将结果落盘。
2PC协议集成在Flink的检查点机制中。检查点分隔符便是启动一个新事务的通知,所有算子中对于它“自身检查点完成”的通知,即是它们的提交投票。JobManager的对于“整个检查点完成”的消息,即为提交事务的指示。
相对于WAL数据汇,2PC数据汇是基于数据汇系统以及数据汇的实现方式,达到精确一次的输出保障。而相对于WAL数据汇的突增写入模式,2PC数据汇为持续向sink 系统写入记录。
不同数据源和数据汇组合所能实现的端到端的一致性保障:
不可重置数据源 | 可重置数据源 | |
---|---|---|
任意数据汇 | 至多一次 | 至少一次 |
幂等性数据汇 | 至多一次 | 精确一次(故障恢复过程中会有临时性不一致) |
WAL数据汇 | 至多一次 | 至少一次 |
2PC数据汇 | 至多一次 | 精确一次 |
内置连接器
Flink为很多外部存储系统都提供了相应的数据读写连接器。消息队列是一类常见的数据流消息来源。在以批处理为主的环境中,我们还经常通过监视文件系统目录并读取其中新增文件的方式来获取数据流。
在数据汇一段,数据流中的事件经常会写入消息队列中,以支撑后续流式应用;或者是写入文件系统,实现归档或支撑后续离线分析及批处理应用;也可以插入到键值存储或数据库系统中,以供查询、搜索或仪表盘应用使用。
Flink为Apache Kafka、Kinesis、RabbitMQ、Apache Cassandra、ElasticSearch、多种文件系统以及JDBC等都提供了相应的连接器。
除了内置的连接器,Flink也支持自定义数据源和数据汇链接器。
- 实现SourceFunction接口和RichSourceFunction抽象类可以自定义非并行的数据源连接器,即只能以单任务云心
- 实现ParallelSourceFunction接口和RichParallelSourceFunction抽象类可用于定义能够同时运行多个任务实例的数据源连接器
- 实现SinkFunction接口和RichSinkFunction抽象类可用于定义数据汇连接器。
异步访问外部系统
除了淡出的手法数据之外,我们还经常需要利用从远程数据库获取的信息来丰富数据流,此时也会涉及和外部存储系统的交互。
Flink提供的AsyncFunction可以有效降低I/O调用所带来的延迟。该函数能够同时发出多个查询并对其结果进行异步处理。它可以通过配置选择对记录进行保存,也可以为了追求更低的延迟按照请求结果的返回顺序处理记录。
为了充分利用AsyncFunction,外部系统最好能够提供一个支持异步调用的客户端,很多现有系统都可以做到这点。而如果外部系统只提供了同步客户端,你可以通过多线程的方式来发送请求并对其进行处理。
第9章 搭建Flink运行流式应用
本章主要介绍Flink集群的多种部署方式以及如何对它进行安全和高可用配置。可参见搭建Flink运行流式应用。略~
第10章 Flink和流式应用运维
运行并管理流式应用
保存点
保存点和检查点的本质相同,二者都是应用状态的一致性完整快照。但他们的声明周期有所差异。检查点会自动创建,在发生故障时自动加载并由Flink自动删除(取决于应用具体配置)。此外,除非应用显式指定要保留检查点,否则它们会在应用取消时自动删除。而保存点则与之相反,它们需要由用户或外部服务手动触发,且永远不会被Flink自动删除。
每个保存点都对应一个持久化数据存储上的目录。它由一个包含了所有任务状态数据文件的子目录和一个包含了全部数据文件绝对路径的二进制元数据文件组成。
通过命令行客户端管理应用
Flink命令行客户端提供了启动、停止和管理Flink应用的功能。它会从./conf/flink-conf.ymal
文件中读取配置。
通过REST API管理应用
REST API可供用户或脚本直接访问,它可以对外公开有关Flink集群和应用的信息,包括指标数据及用于提交和控制应用程序的服务端点等。Flink使用一个Web服务器来同事支持REST API和Web UI,该服务会作为Dispatcher进程的一部分来运行。
控制任务进度
为了实现并行执行,Flink应用会将算子划分为不同的任务,并将这些任务分配到集群中的不同工作进程上。任务分配的目标工作进程,任务的共存情况以及工作进程中的任务数都会对应用性能产生显著影响。
控制任务链接
任务链接指的是将两个或多个算子的并行任务融合在一起,从而可以从它们在同一线程中执行。融合的任务只需通过方法调用就可以进行记录交换,因此几乎没有通信成本。由于任务链接可以提高大多数应用的性能,所以Flink默认会启用它。
然而,也有特定的应用可能无法从中受益。其中一种情况是我们希望将一连串负载较重的函数拆开,让它们在不通的处理槽内执行。Flink支持禁用应用内的任务链接,也可以控制单个算子的链接行为。
定义处理共享槽
Flink默认任务调度策略会将一个完整的程序分片分配到一个处理槽中。根据应用的复杂度以及算子的计算成本,Flink提供了处理槽共享组机制,允许用户手动将任务分配到处理槽中。
调整检查点及恢复
在启用容错功能的情况下,Flink会周期性的将应用状态存储到检查点中。由于在生成检查点时可能需要将大量数据写入持久化存储中,所以其代价可能非常昂贵。增大检查点的生成间隔可以降低常规处理过程中的容错开销,但它同时会使作业在故障恢复过程中需要重新处理更多的数据。
配置检查点
Flink提供了一系列用于调整检查点和状态后端的参数。包括但不限于:指定生成间隔、启用检查点压缩、应用停止后保留检查点等。
配置状态后端
应用状态后端负责维护本地状态,生成检查点和保存点以及在故障时恢复应用状态。因此,应用状态后端的选择和配置对检查点相关操作的性能有很大影响。
Flink默认状态后端是MemorStateBackend。由于它将所有状态保存在内存中,而且检查点全部位于易失且受JVM大小约束的JobManager堆存储内,所以不建议将其用于生产环境。
Flink还支持配置FsStateBackend和RocksDBStateBackend状态后端。
配置故障恢复
当一个拥有检查点的应用发生故障时,它会经过一系列步骤重启,具体包括启动任务、恢复状态(包括数据源任务的读取偏移)和继续处理。为了能够赶得上数据流的进度,应用处理积累数据的速率必须要高于新数据到来的速率。它在追赶进度期间的处理延迟会有所增加。因此,从重启到成功恢复常规处理的进度追赶期间,应用需要足够多的备用资源。
恢复过程除了资源因素,还有两个两个值得关注的主题:重启策略和本地恢复。
重启策略
Flink提供了三种重启策略:
- fixed-delay:已配置的固定时间间隔重试将应用重启某个固定的次数
- failture-rate:允许在未超过故障率的前提下不断重启应用。故障率的定义为某个时间间隔内的最大故障次数
- no-restart:不重启应用,直接失败
本地恢复
Flink支持一种称为本地恢复的特性,能够在应用从相同机器重启时显著提高恢复速度。在启用该功能后状态后端除了将数据写入远程存储系统外,还会将检查点数据在工作进程所在的节点的本地磁盘复制一份。当应用需要重启时,Flink会尝试将相同的任务调度到和之前相同的工作节点执行。如果成功,则任务会优先尝试从本地磁盘加载检查点数据。如果出现任何问题,则将退回到使用远程存储进行处理。
监控Flink集群和应用
Flink Web UI
Flink提供的了解集群和内部作业情况概要最为简单的方式。可以通过http://<jobmanager-hostname>:8081
地址来访问它。
详细分析可参见Flink Web UI分析
指标系统
Flink在默认情况下会收集很多系统和应用指标。指标的手机是按照每个算子、每个TaskManager或JobManager来进行的。
配置日志行为
日志是调试和理解应用行为的另一个重要工具。默认情况下,Flink使用SLF4J日志抽象和log4j日志框架。要修改log4j记录器的属性,可以通过修改conf/目录中的log4j.properties文件来实现。
第11章还有什么?
Flink生态的其他组成部分
用于批处理的DataSet API
Flink可用于实现有界数据的一次性或定期查询。DataSet程序和DataStream程序一样,都是有一系列转换操作组成。二者的不同在于前者是一个有界数据集。
用于关系型分析的Table API及SQL
虽然Flink底层DataStream和DataSet的API是分开的,但你可以使用高层次的关系型API——Table API和SQL,实现流批一体的分析。
用于复杂事件处理和模式匹配的FlinkCEP
FlinkCEP是一个用于复杂事件模式检测的高层次API库。它基于DataStream API实现,允许你指定期望在数据流中检测到的模式。常见的CEP应用场景包括金融应用,欺诈检测,复杂系统中的监控和报警,以及检测网络入侵。
用于图计算的Gelly
Gelly是Flink的图计算API库。它建立在DataSet API和Flink的高效批量迭代之上。它包含了一组常见的图算法,方便日常使用。
本文标签: 《基于Apache Flink的流处理》读书笔记
版权声明:本文标题:《基于Apache Flink的流处理》读书笔记 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://it.en369.cn/IT/1694664655a254771.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论