热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

分布式计算框架Flink核心基石介绍

Flink作为主流的分布式计算框架,满足批流一体、高吞吐低时延、大规模复杂计算、

Flink作为主流的分布式计算框架,满足批流一体、高吞吐低时延、大规模复杂计算、高可靠的容错和多平台部署能力。前文中介绍了Flink的数据流处理流程以及基本部署架构和概念,本文将对Flink中的核心基石进行深入介绍。


3、Flink核心基石

在流处理场景中,数据源源不断的流入系统,衡量流处理快和量两方面的性能,一般使用延迟(Latency)和吞吐(Throughput)两个指标。

  • 延迟表示一个事件被系统处理的总时间
  • 吞吐表示一个系统最多能处理多少事件,一般以单位时间处理的事件数量为标准

延迟和吞吐是衡量流处理引擎的重要指标,在Flink流式计算引擎中,引入4个核心的机制以满足低延迟高吞吐的指标。这四个基石是Checkpoint、State、Time和Window:

  • State:提供了丰富的State API,ValueState、ListState、MapState、BroadcastState等
  • Checkpoint:基于Chandy-Lamport算法,实现分布式一致性快照,提供了一致性语义
  • Time:提供了Watermark机制和Event Time、Process Time和Ingestion Time三种时间语义
  • Window:实现滚动、滑动、会话窗口
3.1 State状态

Flink中定义了State,用来保存中间计算结果或者缓存数据。根据是否需要保存中间结果分为无状态计算和有状态计算。在批处理过程中,数据是划分为块分片去完成的,然后每一个Task去处理一个分片。当分片执行完成后,把输出聚合起来就是最终的结果。在这个过程当中,对于state的需求还是比较小的。对于流计算而言,事件持续不断的产生,如果每次计算都是相互独立的,不依赖上下游的事件,则是无状态计算;如果计算需要依赖于之前或者后续的事件,则是有状态的计算。

在Flink中使用State的典型场景如下:

  • 数据流中的数据有重复,想对数据去重,需要记录哪些数据已经流入应用,当新数据流入时,根据已流入数据来判断是否去重
  • 检查输入流是否符合某个特定的模式,需要将之前流入的元素以状态的形式缓存下来。比如判断温度传感器中的数据流的温度是否在持续上升
  • 对一个时间窗口内的数据进行聚合分析,使用增量计算方式计算当前窗口内的数值总和并保存为中间结果,当有新的窗口到来时只需要在中间结果的基础上求和即可
  • 在线机器学习的场景下,需要根据新流入数据不断更新机器学习的模型参数

Flink中提供了对State操作的接口,用户在开发Flink应用的时候,可以将临时数据保存在State中,同时利用checkpoint机制对state进行备份,一旦出现异常能够从保存的State中恢复状态,实现Exactly-Once。另外,对state的管理还需要注意以下几点:

  • 状态数据的存储和访问:在Task内部如何高效的保存状态数据以及使用状态数据
  • 状态数据的备份和恢复:状态信息如何高效的备份,并且在异常时候能够恢复到Task之前的状态
  • 状态数据的划分和动态扩容:在并行Task过程中如何对状态数据进行切分,并且在并行度修改的时候能够正确恢复到对应的Task
  • 状态数据的清理:状态数据的保存和使用是有成本的,对于过期数据的清理是有必要的
3.1.1 Flink中的State类型

Flink中有两种类型的State:Keyed State和Operator State。每种State有两种基本的形式:Managed State和Raw State,Managed State是由Flink管理的,Flink负责存储、恢复和优化;Raw State是由开发者管理的,需要用户自己进行序列化。通常,在DataStream上的状态,推荐使用Managed State,当实现一个用户自定义的Operator的时候,会使用到Raw State。

1)Keyed State

Keyed State是KeyedStream上的状态,其中state是以Key/Value的方式存储,并严格按照state operator操作的数据流进行划分和分布。假如输入流按照ID为key进行keyBy()分组,形成一个KeyedStream,数据流中所有keyID为1的数据共享一个状态,可以访问和更新这个状态,以此类推,每个Key对应一个状态。

在这里插入图片描述

2)Operator State

Operator State可以用在所有算子上,每个算子子任务共享一个状态,流入这个算子的子任务的所有数据都可以访问和更新这个状态。

在这里插入图片描述

无论是Keyed State还是Operator State,Flink的状态都是基于本地的,即每个算子子任务维护着自身的状态,不能访问其它算子子任务的状态。Keyed State和Operator State的区别总结如下:

特点Keyed StateOperator State
适用算子类型只适用于KeyedStream上算子适用于所有算子
状态分配每个Key对应一个状态一个算子Task对应一个状态
创建和访问方式重写Rich Function,通过里面的RuntimeContext访问实现CheckpointedFunction等接口
横向扩展状态随着Key自动在多个算子子任务上迁移有多种状态重新分配的方式
支持的数据类型ValueState、ListState、MapState等ListState、BroadcaseState等
3.2 Checkpoint和Savepoints
3.2.1 Checkpoint

Flink定期将状态数据持久化到存储,故障发生后从之前的备份中恢复数据,这个过程称为Checkpoint。Checkpoint为Flink提供了exactly-once的保障,可以理解为Flink在某一特定时刻的全局状态快照,包含了所有的Task/operator的状态。

Flink基于Chandy-Lamport算法实现了一个分布式的一致性快照,从而提供了一致性的语义。Checkpoint大致流程如下:

  1. 暂停处理新流入数据,将新数据缓存起来
  2. 将算子Task的本地状态数据复制到持久化的存储空间
  3. 持久化完成后,继续处理新流入的数据,包括刚刚缓存的数据

默认情况下Checkpoint机制是关闭的,需要调用env.enableCheckpointing(n)来开启,表示每隔n毫秒进行一次Checkpoint。Checkpoint是一个高负载的任务,需要设置合理的频率,设置过小可能上次checkpoint尚未完成下次的checkpoint已经开始;如果设置过大,checkpoint的频率更少消耗的系统资源也会更少,但是故障重启或恢复时,需要处理更多的数据。

3.2.2 Barriers

在Checkpoint过程中有个Checkpoint Barrier的概念,checkpoint分界线是插入到数据流中将数据流切分成不同的段,并作为数据流的一部分向下流动。Flink中的Checkpoint逻辑是,算子接收到Barrier后,会对状态进行snapshot,每个Checkpoint Barrier有一个ID,表示该段数据属于哪个checkpoint。

在这里插入图片描述

Barriers是Flink快照的核心要素,它们inject到数据流中而不会影响流量,并且barriers永远不会超过记录。同时来自不同快照的多个barriers可以同时在流中出现,这意味着可以同时发生各种快照。如上图所示:

  • 出现一个Barrier,在该Barrier之前出现的记录都属于该Barrier对应的Snapshot,在该 Barrier之后出现的记录属于下一个Snapshot
  • 来自不同Snapshot多个Barrier可能同时出现在数据流中,也就是说同一个时刻可能并发生成多个Snapshot
  • 当一个中间(Intermediate)Operator接收到一个Barrier后,它会发送Barrier到属于该Barrier的Snapshot的数据流中,等到Sink Operator接收到该Barrier后会向Checkpoint Coordinator确认该Snapshot,直到所有的Sink Operator都确认了该Snapshot,才被认为 完成了该Snapshot

Barrier根据是否缓存channel中的数据又分为两类:Barrier buffer和Barrier Track

  • Barrier Buffer将input channel接收到barrier后缓存并阻塞后续流入的数据,直到所有的barrier都接收到或者不满足特定的检查点的条件后,才会释放这些被阻塞的channel。这种机制也称为aligned Checkpoint,正是这种机制来实现 EXACTLY_ONCE 的一致性。
  • BarrierTrack的实现就要简单地多,它仅仅是对数据流中的barrier进行跟踪,但是数据流中的buffer是直接放行的。这种情况也称为unaligned Checkpoint,会导致同一个检查点中可能会预先混入后续检查点的元素,从而只能提供AT_LEAST_ONCE的一致性。

基于Stream Aligning操作能够实现Exactly Once语义,但是也会给流处理应用带来延迟,因为aligned Barrier,会暂时缓存一部分Stream的记录到Buffer中,尤其是在数据流并行度很高的场景下可能更加明显,通常以最迟对齐Barrier的一个Stream为处理Buffer中缓存记录的时刻点。在Flink中,提供了一个开关,选择是否使用Stream Aligning,如果关掉则Exactly Once会变成At least once。

3.2.3 Savepoints

Checkpoint机制是为了故障重启的时候,使得作业中的状态数据与故障重启之前报错一致,是一种故障恢复的保护能力。Savepoints则是手动备份数据,以便进行调试、迁移,是协助开发调试的功能。Savepoints相关操作是有计划的,一般由开发者手动触发、管理和删除,比如将当前状态保存下来后,可以更新并行度、修改逻辑代码,甚至进行A/B测试等。

3.3 Time
3.3.1 Time解读

在流处理中,时间是一个核心的概念,是整个系统的基石。在Flink中,时间有三种类型:

在这里插入图片描述

  • Event Time:是事件创建的时间。它通常由事件中的时间戳描述,例如采集的日志数据中,每一条日志都会记录自己的生成时间,Flink通过时间戳分配器访问事件时间戳。
    • 使用Event Time,在理想情况下可以一直等待所有事件到达后再进行时间窗口的处理,最终结果是正确且一致的,并且不用担心乱序的问题。但是实际应用中,在涉及到按时间窗口进行统计时,会将窗口内的事件缓存下来,直到接收到一个watermark。Watermark意味着在一个时间窗口下,Flink会等待一个有限的时间,这在一定程度上降低了计算结果的绝对准确性,而且增加了系统的延迟。
  • Ingestion Time:是数据进入Flink的时间。从源端到下游各个算子中间可能有很多计算环节,任何一个算子处理速度的快慢都可能影响到下游算子的Processing Time。而Ingestion Time是数据流最早进入Flink的时间,因此不会被算子的处理效率影响。
    • Ingestion Time在概念上是位于Event Time和Processing Time之间,比Processing Time稍早,不需要指定Watermark
  • Processing Time:是每一个执行基于时间操作的算子的本地系统时间,与机器相关,默认的时间属性就是Processing Time。在Processing Time时间窗口下,无论事件什么时候发生,只要该事件在某个时间段到达了某个算子,就会被归结为该窗口。
    • Processing Time只依赖当前节点的操作系统时间,无需缓存,实现起来更简单,延迟更小。

在Flink的流式处理中,绝大部分的业务都会使用EventTime,一般只在EventTime无法使用时,才会被迫使用ProcessingTime 或者IngestionTime。

3.3.2 Watermark

Flink的三种时间语义中,只有Event Time需要设置Watermark。流式数据从产生到处理中间经过了很多过程,中间因为网络等原因可能会出现乱序,导致Flink接收到的事件的先后顺序不是严格按照Event Time的先后顺序排了的。一旦出现乱序,如果只根据EventTime决定window的运行,不能明确数据是否全部到位,但又不能无限期的等下去,此时必须要有个机制来保证一个特定的时间后,必须触发window去进行计算了,这个特别的机制,就是 Watermark。Watermark本质上是Flink插入到数据流中的一种特殊的数据结构,它包含一个时间戳,并假设后续不会有小于该时间戳的数据。

  • 下图显示了带有(逻辑)时间戳和内联Watermark的事件流。事件是按顺序排列的(相对于它们的时间戳),这意味着水印只是流中的周期性标记。

在这里插入图片描述

  • 下图显示了事件没有按照时间戳排序,通常,Watermark是一种声明,通过流中的该点,到达某个时间戳的所有事件都应该到达。一旦Watermark到达算子,算子就可以将其内部事件时间提前到Watermark的值

在这里插入图片描述

3.3.3 分布式中的Watermark

在实际的流计算中一个作业往往会处理多个Source的数据,每个并行的子任务会生成单独的watermark。这些不同的watermark在各自的source内是单调递增的,但是汇聚到一起时可能不是单调递增的。此时,Flink会选择所有流入的EventTime中最小的一个向下游流出,从而保证Watermark的单调递增和数据完整性。

在这里插入图片描述

如图所示,Source算子生成各自的Watermark,并随着数据流向下游的map算子,map算子是无状态计算,所以会将Watermark向下透传。Window算子收到上游两个输入的watermark后,选择其中一个较小的发送给下游。Window(1)算子比较Watermark 29和Watermark 14,选择Watermark 14作为算子当前的Watermark发送给下游,Window(2)算子采用同样的处理逻辑。

3.4 Window窗口

流式数据中对事件的汇总统计不同于一般的批处理,比如说统计流式数据中的事件总数是不现实的,因为数据是无边界的。但是使用Window将一个无限stream切割为有限大小的窗口,然后在这些窗口内进行计算,因此window是一种切割无限数据为有限块进行处理的手段。通常划分window有两种方式:time-driven-window和data-driven-window

  • 根据时间划分(time-driven-window),比如每1分钟统计一次;
  • 根据数据划分(data-driven-window),比如每100个数据统计一次

在这里插入图片描述

3.4.1 Windows类型

在Flink中提供了三种默认的Windows类型:滚动窗口(Tumbling Window)、滑动窗口(Sliding Window)和会话窗口(Session Window)

在这里插入图片描述

1)滚动窗口

将数据依据固定的长度对数据进行切分。滚动窗口分配器将每个元素分配到一个指定窗口大小的窗口中,滚动窗口有一个固定的大小,并且不会出现重叠。这个固定的大小可以是固定的时间范围或者固定的数据量大小

  • 滚动计数窗口:累计固定个数的元素视为一个窗口,该类型的窗口无法像时间窗口一样事先切分好

val tumblingCnts: DataStream[(Int, Int)] = vehicleCnts
// key stream by sensorId
.keyBy(0)
// tumbling count window of 100 elements size
.countWindow(100)
// compute the carCnt sum
.sum(1)

  • 滚动时间窗口:表示在时间上按照事先约定的窗口大小切分的窗口,窗口之间不会相互重叠

val tumblingCnts: DataStream[(Int, Int)] = vehicleCnts
// key stream by sensorId
.keyBy(0)
// tumbling time window of 1 minute length
.timeWindow(Time.minutes(1))
// compute sum over carCnt
.sum(1)

2)滑动窗口

由固定的窗口长度和滑动间隔组成。滑动窗口分配器将元素分配到固定长度的窗口中,与滚动窗口类似,窗口的大小由窗口大小参数来配置,另一个窗口滑动参数控制滑动窗口开始的频率。因此,滑动窗口如果滑动参数小于窗口大小的话,窗口是可以重叠的,在这种情况下元素会被分配到多个窗口中。

  • 滑动计数窗口:累积固定个数的元素视为一个窗口,每超过一定个数,则产生一个新的窗口

val slidingCnts: DataStream[(Int, Int)] = vehicleCnts
.keyBy(0)
// sliding count window of 100 elements size and 10 elements trigger interval
.countWindow(100, 10)
.sum(1)

  • 滑动时间窗口:表示在时间上按照事先约定的窗口大小、滑动步长切分的窗口,滑动窗口之间可能会存在相互重叠的情况

val slidingCnts: DataStream[(Int, Int)] = vehicleCnts
.keyBy(0)
// sliding time window of 1 minute length and 30 secs trigger interval
.timeWindow(Time.minutes(1), Time.seconds(30))
.sum(1)

3)会话窗口

一种特殊的窗口,当超过一段时间该窗口没有收到新的数据,则视为该窗口结束,所以无法事先确定窗口的长度、元素个数,窗口之间也不会相互重叠。

3.4.2 Windows工作机制

在这里插入图片描述

Flink中提供了很多窗口算子负责处理窗口,在整个处理过程中可能会存在多个窗口。窗口机制如上图所示:

  1. 每个数据元素进入窗口时,会被交给WindowAssigner,WindowAssigner决定元素被放在哪个窗口,在这过程中可能创建新的窗口或合并旧的窗口。在窗口操作过程中可能同时存在多个窗口,而一个元素也可能被放入多个窗口中。

// create windowed stream using a WindowAssigner
var windowed: WindowedStream[IN, KEY, WINDOW] = keyed
.window(myAssigner: WindowAssigner[IN, WINDOW])

  1. 每个窗口拥有一个Trigger,Trigger上有定时器,用来决定窗口何时被计算或purge。每当有元素进入窗口或者之前注册的定时器超时,都会调用Trigger

// override the default trigger of the WindowAssigner
windowed = windowed.trigger(myTrigger: Trigger[IN, WINDOW])

  1. Trigger触发后,窗口中的元素集合会交给Evictor,Evictor主要用来遍历窗口中的元素列表并决定最先进入窗口的多少个元素需要被移除,剩余的元素会交给用户指定的函数进行窗口的计算。如果没有Evictor,窗口中的所有元素会一起交给函数进行计算

// specify an optional evictor
windowed = windowed.evictor(myEvictor: Evictor[IN, WINDOW])

  1. 计算函数收到窗口中的元素进行计算,计算后的结果会输出给下游。Flink中的计算函数有很多,比如sum()、min()、max()等

// apply window function to windowed stream
val output: DataStream[OUT] = windowed
.apply(myWinFunc: WindowFunction[IN, OUT, KEY, WINDOW])

4、总结

分布式计算框架从最早的Hadoop MapReduce批量处理,到第二代的DAG框架,再到第三代的Spark批量和流式处理以及Storm流式处理,发展到第四代的Flink批流一体的处理架构,实现了低时延高吞吐、高可靠以及可扩展的分布式计算平台。


参考资料:

  1. https://flink.apache.org/zh/
  2. https://blog.csdn.net/a805814077/article/details/108095451
  3. https://blog.csdn.net/helloHbulie/article/details/120333974
  4. 《Flink原理与实践》,鲁蔚征著
  5. 《Flink内核原理与实现》,冯飞著
  6. https://nightlies.apache.org/flink/flink-docs-release-1.15/zh/docs/concepts/time/
  7. https://nightlies.apache.org/flink/flink-docs-release-1.15/zh/docs/concepts/stateful-stream-processing/
  8. https://flink.apache.org/news/2015/12/04/Introducing-windows.html
  9. https://mp.weixin.qq.com/s?__biz=Mzg2MzU2MDYzOA

版权声明:本文为solihawk原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/solihawk/article/details/126751696
推荐阅读
  • 清华大学出版社 | 杨丹:基于MATLAB机器视觉的黑色素瘤皮肤癌检测技术及源代码分析(第1689期)
    清华大学出版社 | 杨丹:基于MATLAB机器视觉的黑色素瘤皮肤癌检测技术及源代码分析(第1689期) ... [详细]
  • 为了确保iOS应用能够安全地访问网站数据,本文介绍了如何在Nginx服务器上轻松配置CertBot以实现SSL证书的自动化管理。通过这一过程,可以确保应用始终使用HTTPS协议,从而提升数据传输的安全性和可靠性。文章详细阐述了配置步骤和常见问题的解决方法,帮助读者快速上手并成功部署SSL证书。 ... [详细]
  • 在Cisco IOS XR系统中,存在提供服务的服务器和使用这些服务的客户端。本文深入探讨了进程与线程状态转换机制,分析了其在系统性能优化中的关键作用,并提出了改进措施,以提高系统的响应速度和资源利用率。通过详细研究状态转换的各个环节,本文为开发人员和系统管理员提供了实用的指导,旨在提升整体系统效率和稳定性。 ... [详细]
  • 本文探讨了如何利用Java代码获取当前本地操作系统中正在运行的进程列表及其详细信息。通过引入必要的包和类,开发者可以轻松地实现这一功能,为系统监控和管理提供有力支持。示例代码展示了具体实现方法,适用于需要了解系统进程状态的开发人员。 ... [详细]
  • 本文深入解析了Java 8并发编程中的`AtomicInteger`类,详细探讨了其源码实现和应用场景。`AtomicInteger`通过硬件级别的原子操作,确保了整型变量在多线程环境下的安全性和高效性,避免了传统加锁方式带来的性能开销。文章不仅剖析了`AtomicInteger`的内部机制,还结合实际案例展示了其在并发编程中的优势和使用技巧。 ... [详细]
  • Spring框架的核心组件与架构解析 ... [详细]
  • 在《Linux高性能服务器编程》一书中,第3.2节深入探讨了TCP报头的结构与功能。TCP报头是每个TCP数据段中不可或缺的部分,它不仅包含了源端口和目的端口的信息,还负责管理TCP连接的状态和控制。本节内容详尽地解析了TCP报头的各项字段及其作用,为读者提供了深入理解TCP协议的基础。 ... [详细]
  • 本文深入解析了WCF Binding模型中的绑定元素,详细介绍了信道、信道管理器、信道监听器和信道工厂的概念与作用。从对象创建的角度来看,信道管理器负责信道的生成。具体而言,客户端的信道通过信道工厂进行实例化,而服务端则通过信道监听器来接收请求。文章还探讨了这些组件之间的交互机制及其在WCF通信中的重要性。 ... [详细]
  • Python 伦理黑客技术:深入探讨后门攻击(第三部分)
    在《Python 伦理黑客技术:深入探讨后门攻击(第三部分)》中,作者详细分析了后门攻击中的Socket问题。由于TCP协议基于流,难以确定消息批次的结束点,这给后门攻击的实现带来了挑战。为了解决这一问题,文章提出了一系列有效的技术方案,包括使用特定的分隔符和长度前缀,以确保数据包的准确传输和解析。这些方法不仅提高了攻击的隐蔽性和可靠性,还为安全研究人员提供了宝贵的参考。 ... [详细]
  • 本文深入探讨了NoSQL数据库的四大主要类型:键值对存储、文档存储、列式存储和图数据库。NoSQL(Not Only SQL)是指一系列非关系型数据库系统,它们不依赖于固定模式的数据存储方式,能够灵活处理大规模、高并发的数据需求。键值对存储适用于简单的数据结构;文档存储支持复杂的数据对象;列式存储优化了大数据量的读写性能;而图数据库则擅长处理复杂的关系网络。每种类型的NoSQL数据库都有其独特的优势和应用场景,本文将详细分析它们的特点及应用实例。 ... [详细]
  • 本文详细介绍了在 Oracle 数据库中使用 MyBatis 实现增删改查操作的方法。针对查询操作,文章解释了如何通过创建字段映射来处理数据库字段风格与 Java 对象之间的差异,确保查询结果能够正确映射到持久层对象。此外,还探讨了插入、更新和删除操作的具体实现及其最佳实践,帮助开发者高效地管理和操作 Oracle 数据库中的数据。 ... [详细]
  • 如何优化MySQL数据库性能以提升查询效率和系统稳定性 ... [详细]
  • 单链表的高效遍历及性能优化策略
    本文探讨了单链表的高效遍历方法及其性能优化策略。在单链表的数据结构中,插入操作的时间复杂度为O(n),而遍历操作的时间复杂度为O(n^2)。通过在 `LinkList.h` 和 `main.cpp` 文件中对单链表进行封装,我们实现了创建和销毁功能的优化,提高了单链表的使用效率。此外,文章还介绍了几种常见的优化技术,如缓存节点指针和批量处理,以进一步提升遍历性能。 ... [详细]
  • ButterKnife 是一款用于 Android 开发的注解库,主要用于简化视图和事件绑定。本文详细介绍了 ButterKnife 的基础用法,包括如何通过注解实现字段和方法的绑定,以及在实际项目中的应用示例。此外,文章还提到了截至 2016 年 4 月 29 日,ButterKnife 的最新版本为 8.0.1,为开发者提供了最新的功能和性能优化。 ... [详细]
  • 在 Kubernetes 中,Pod 的调度通常由集群的自动调度策略决定,这些策略主要关注资源充足性和负载均衡。然而,在某些场景下,用户可能需要更精细地控制 Pod 的调度行为,例如将特定的服务(如 GitLab)部署到特定节点上,以提高性能或满足特定需求。本文深入解析了 Kubernetes 的亲和性调度机制,并探讨了多种优化策略,帮助用户实现更高效、更灵活的资源管理。 ... [详细]
author-avatar
建中姿吟7523
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有