Flink 架构与应用漫聊
一、前言
在如今互联网用户、移动设备、LOT 设备、服务等激增的时代下,其产生的数据体量及速率早已不同日而语了。如在刚刚过去的阿里双十一流量洪峰,在 Flink 实时计算技术的驱动下全程众享丝滑。阿里的实时计算峰值可达到恐怖的 4.5+亿次/秒,且数据量也达到了惊人的 7TB/秒,然而这么强悍的计算能力背后都离不开 Flink 的支撑。
Flink 已无需再证明自己的能力和价值,所以作为一个大数据工程师你还在苦啃 Spark、Hadoop MR、Strom,却还没搞过 Flink?不要让自己 out 了,赶紧跟着本文来好好体验一把,领会其中的奥义吧。
二、简介
2.1 久闻 Flink 大名,那它到底是什么呢?
图 1 Flink Logo
Apache Flink is a framework and distributed processing engine for stateful computations over unbounded and bounded data streams. Flink has been designed to run in all common cluster environments, perform computations at in-memory speed and at any scale.(摘自官网对于 Flink 的定义)
官方给的定义极其简洁但阐述的非常清晰,我们来解读一下,大致意思是 Apache Flink 是一个框架和分布式处理引擎,用于对无界和有界数据流进行状态处理,能运行在常见的集群中,可在任何规模的集群中以内存形式进行分布式计算。
根据定义可以明确的是 Flink 是一个分布式计算引擎,处理有界和无界的数据流并且可以做到有状态处理,并且还支持内存级别的计算。
2.2 Flink 有哪些应用场景?
基于 Flink 的定义我们可以很清晰的分析出它的应用场景,如电商行业的数据报表、双十一的营业额实时统计,物联网(LOT)的传感器数据采集和统计,金融行业的实时对账计算、异常检测,应用的日志实时监控、告警等。总结来说数据产生大且速率快,并且对于计算实时性要求高的情况下均可考虑用 Flink 作为支撑。
2.3 和 SparkStreaming 对比
提到分布式计算那必然有 Spark 的一席之地,Spark 问世后,受到了大数据开发者的强烈反响,收获了大量的好评,一度认为是开发者的福音,Spark 不但生态完整(包含 Spark Core、SparkSQL、SparkStreaming、MLlib、GraphX),和 Hadoop 生态整合方便,最重要的是 Spark 基于 RDD 模型的分布式计算速度比 Hadoop MR 提高了不少。这里不过多赘述 Spark 生态的内容,着重关注 SparkStreaming 和 Flink 的一些区别。
两者最大的差异在于运行模型,Spark Streaming 是微批处理,运行的时候需要指定批处理的时间,每次运行 job 时处理一个批次的数据。
图 2 spark 数据处理模型 来源 spark 官网
Flink 是基于事件驱动的,事件可以理解为消息。事件驱动的应用程序是一种状态应用程序,它会从一个或者多个流中注入事件,通过触发计算更新状态,或外部动作对注入的事件作出反应。
图 3 数据处理模型 来源 flink 官网
除了与运行模型,两者的任务调度、时间机制、容错机制上都有所差异,这一节就不具体描述了,在下文会有所提及。
2.4 Flink 的其他特点
事件时间语义机制支持丰富,如事件事件(event-time)、处理事件(processing-time);
精确一次(exactly-once)的状态一致性保证机制健全;
与常用存储系统兼容性好(如 ElasticSearch、Kafka、Redis 等);
高可用、动态扩展,能运行在 Hadoop 组件上。
三、运行架构
3.1 运行时组件介绍:
ResourceManager(资源管理器):主要负责 Flink 运行时资源管理,包括 TaskManager、JobManger 的资源申请和释放工作
JobManager(作业管理器):控制任务运行的主进程,包括生成作业图(JobGraph)、逻辑数据流图(Logical DataFlow Grap)、执行图(ExecutionGraph),负责将任务发送到 TaskManager 上执行,JobManager 会负责所有需要中央协调的操作。
TaskManager(任务管理器):真正运行任务的资源,每个 TaskManager 都包含一定数量的 slot(任务执行的最小单位),在执行过程中可以跟其他运行同一应用程序的 TaskManager 交换数据。
3.2 运行时架构(基于 Yarn 模式):
图 4 yarn 模式下运行架构
Flink 任务提交后,客户端会向 HDFS 上传 Flink 的 Jar 包和配置信息。(因为 Flink 是分布式计算,我们提供的 Jar 包需要每个计算节点都能访问);
将任务提交到 Yarn ResourceManager;
ResourceManager 申请集群资源并分配 Container 资源给对应的 NodeManager,启动 ApplicationMaster,ApplicationMaster 加载 Flink 任务,启动 JobManager,作为 Flink 任务的一个客户端;
JobManager 经过分析生成 ExecutionGraph 物理执行图,ApplicationMaster 根据物理执行图向 ResourceManager 申请对应的 TaskManager 资源;
ResourceManager 分配 Container 资源后,由 ApplicaitonMaster 通知对应 NodeManager 资源启动 TaskManager;
NodeManager 加载 HDFS 上的 Flink Jar 包和构建运行环境启动 TaskManager;
TaskManager 启动后与 JobManager 保持心跳,等待 JobManager 给自己分配计算任务。
四、用法详解
阅读到这里,相信大家在脑海里对于 Flink 已有了一些自己的认知,想跃跃欲试了?那么本节就重点来讲讲 Flink 有哪些优秀功能及如何使用它?
4.1 如何创建一个 Flink 应用?
图 5 创建 flink 应用步骤
如上图所示 Flink 程序由 Source,Transformation、Sink 三个核心组件组成。
图中 addSource()、addSink()分别表示数据流输入和输出的底层抽象,所以自定义输入和输出会变得非常简单,只需实现底层抽象即可,如作为输入数据流的常用组件 HDFS、Kafka 都有官方实现,输出数据官方支持同样也很丰富,如 ElasticSearch、Kafka、Redis、HDFS 等。
4.2 什么是数据流窗口(Window)计算?
在流处理应用中,数据是源源不断发送,来一条处理一条不等待,这也是 Flink 的一大优势,那似乎想对某个时间段内的数据发生计算变的不是很容易(如统计出在过去一分钟内对某个网页的访问量)?因此衍生出了窗口这个概念,Flink 认为 Batch 是 Streaming 的一个特例,并不影响 Flink 底层还是一个流式处理引擎的设计初衷。而窗口恰好就是从 Streaming 到 Batch 的一个桥梁。所以窗口就是将源源不断的数据流根据你定义的规则划分出不同的区间,每个区间的数据都到齐之后发生最终的窗口计算。Fink 内部提供了非常完善的窗口机制,也是 Flink 的最大的亮点之一。下面来介绍一下窗口的几种类型。
时间窗口 Time Window(支持时间语义 event-time、processing-time):
滚动时间窗口:能将数据流切分成不重叠的窗口,每个事件只能属于一个窗口;
滑动时间窗口:一个元素可以对应多个窗口,当窗口每次滑动的长度等于窗口大小,效果和滚动事件窗口效果一致;
会话窗口:开启一个窗口,窗口在会话时间段内未受到消息,则视为窗口结束。
计数窗口 Count Window(按照元素个数定义):
滚动计数窗口:按照自定义的窗口元素个数来切分成不重叠的窗口,每个事件只能属于一个窗口
滑动计数窗口:一个可能元素对应多个窗口,可定义窗口元素个数和滑动元素个数 code:val ds: DataStream[String] = env.addSource(new CustomizeSource())
ds.timewindow(Time.seconds(15)) 滚动时间窗口
ds.timewindow(Time.seconds(15), Time.seconds(15)) 滑动时间窗口
ds.window(EventTimeSessionWindows.withGap(Time.minutes(10)))会话窗口
ds.countwindow(10) 滚动计数窗口
ds.countwindow(10,2) 滑动计数窗口
在章节 2 Flink 简介中我们提到了“有界”、“无界”的概念,那它和窗口有什么联系吗?
图 6 数据流定义 来源 flink 官网
由上图可以看出 bounded stream(有界)数据流是有起点和结束点的,而 unbounded stream(无界)只有 start of the stream(数据起点)并没有看到结束点。所以窗口计算就可以看成是无界的数据流切割为有界流的一种方式,它会将流数据分发到有限大小的桶(bucket)中进行分析。
使用过 Spark 的同学都知道它一般都是针对大数据做离线计算分析,包括 SparkStreaming 的微批处理模式,本质上都是对有界数据流的一种分布式计算处理。
4.3 时间语义与 watermark
在流处理中时间概念至关重要,因为事件总是在某个时间点或者时间段内发生,所以流数据计算和时间息息相关,例如 4.2 提到的窗口聚合计算、会话计算等。Flink 提供了丰富的时间语义支持。
事件时间模式(event-time):提取数据自带的时间,因此,无论处理的是历史记录的事件还是实时的事件,事件时间模式的处理总能保证结果的准确性和一致性。
处理时间模式(processing-time):根据处理引擎的机器时钟触发计算,一般适用于有着严格的低延迟需求,并且能够容忍近似结果的流处理应用。
在实际编码过程中,可根据业务场景选择对应的时间语义。如需要监控日志异常信息,日志数据往往都自带 event-time,这时候采用事件时间模式是最准确的。当然如果业务上并不关心事件发生的时间而更注重在窗口上的计算场景,使用处理时间是最佳的选择。
聊完了时间语义,来试想一个场景,在实际应用场景中,数据一般都是从大集群、分布式环境中不同的数据节点采集而来的,数据很有可能是乱序到达,那在窗口计算过程中能保证在窗口关闭时所有的数据都到齐了吗?显然是不能保证的。针对这种场景 Flink 引入了 watermark 的概念,watermark 是一种平衡处理延迟和完整性的灵活机制,当带有 watermark 的事件时间模式处理数据流时,窗口能等待迟到的数据来之后再发生计算,或者将这些数据重定向到侧输出流。
4.4 状态管理
假如我们的计算只针对每一个单独的事件上的转换操作,那我们并不需要状态。可往往并没有那么简单,计算过程通常会产生依赖关系。所以中间的计算结果需要用状态来保存,以供后续的某个时间点进行访问和处理。
图 7 状态管理 来源 flink 官网
Flink 提供了许多状态管理支持:
多种状态基础类型:Flink 为多种不同的数据结构提供了相对应的状态基础类型,例如原子值(value),列表(list)以及映射(map)。开发者可以基于处理函数对状态的访问方式,选择最高效、最适合的状态基础类型。
插件化的 State Backend:State Backend 负责管理应用程序状态,并在需要的时候进行 checkpoint。Flink 支持多种 state backend,可以将状态存在内存或者 RocksDB。RocksDB 是一种高效的嵌入式、持久化键值存储引擎。Flink 也支持插件式的自定义 state backend 进行状态存储。
精确一次语义(exactly-one):Flink 的 checkpoint 和故障恢复算法保证了故障发生后应用状态的一致性。因此,Flink 能够在应用程序发生故障时,对应用程序透明,不造成正确性的影响。超大数据量状态:Flink 能够利用其异步以及增量式的 checkpoint 算法,存储数 TB 级别的应用状态。
可弹性伸缩的应用:Flink 能够通过在更多或更少的工作节点上对状态进行重新分布,支持有状态应用的分布式的横向伸缩。
定义状态代码实现:
//定义一个 long 类型的 ValueState 状态
lazy val timerState: ValueState[Long] = getRuntimeContext.getState(new ValueStateDescriptor[Long]("timerState", classOf[Long]))
4.5 Table API 与 Flink SQL
Table API 是一套内嵌在 Java 和 Scala 语言中的查询 API,它允许以非常直观
的方式组合来自一些关系运算符的查询
Flink 的 SQL 支持基于实现了 SQL 标准的 Apache Calcite
图 8 Api 模型 来源 flink 官网
由图可看出,TableApi 和 SQL 是对 DataStream Api 更高层的一个实现,它可以将数据流转换成数据表的形式,通过写 SQL 的形式对数据进行分析,对于习惯 SQL 开发的大数据开发工程师来说是一个福音。对于曾经熟练使用 SparkSQL 的开发者来也可以无缝切换,降低了学习成本。下面来看看如何简单应用。
图 9 table api 和 flink sql 代码实例
Flink 对于 UDF 函数的实现也非常完善,除了内置的 substring、max、min、avg、sum 常用函数之外,还支持自定义 UDF 函数,如标量函数、聚合函数、表值函数。这里在着重解释表值函数
图 10 表值函数实现
熟悉 Hive 的同学都知道 explode + lateral view 的用法,通常也叫侧写函数,这里的表值函数就是侧写函数的一种实现。
举例:原表数据
经过表值函数查询可得到结果
五、结束语
通过上述 4 个章节的讲解与用法分析,相信在大家的脑海中留下了一些印象,最后我们对 Flink 再做一个总结性的回顾。
Flink 是一个流式数据分布式处理引擎,具有高吞吐、低延迟的显著特性。并可以实现状态管理编程和窗口计算(watermark 解决数据乱序问题),生态完善(集成 Kafka、ES、HDFS 简单),其中 TableApi 与 FlinkSQL 能通过编写 SQL 语句来实现分布式计算。对于高可用来说,Flink 拥有丰富的容错机制及故障恢复后的数据一致性问题解决方案。
因此 Flink 已经成为我们实时计算领域的带头大哥,不解释任何反驳。对于有类似业务场景的同学赶紧去实战一把吧!
注:本文部分图片和文案摘自官方和网络,仅供读者参考。
版权声明: 本文为 InfoQ 作者【数新网络官方账号】的原创文章。
原文链接:【http://xie.infoq.cn/article/06fe8a6deced490ccbd8a4338】。文章转载请联系作者。
评论