Flink 原理详解
Flink 原理详解
Apache Flink是一个开源的流处理框架,支持流处理和批处理,具有低延迟、高吞吐、容错和可扩展等特点。本文将详细介绍Flink的基本原理、架构设计、编程模型以及与Spark Streaming的对比分析。
Flink基本概念
Flink是一个流处理框架,支持流处理和批处理。其特点是流处理有限、可容错、可扩展、高吞吐和低延迟。
- 流处理:处理一条数据后,立即在下一个节点从缓存中取出数据进行计算。
- 批处理:只有在处理完一批数据后,才会通过网络传输到下一个节点。
流处理的优点是低延迟,批处理的优点是高吞吐。Flink同时支持这两种处理模式,其网络传输设计固定缓存块为单位,用户可以设置缓存块的超时值来决定缓存块什么时候进行传输。当数据大于0时进行处理就是流式处理,如果设置为无限大就是批处理模型。
Flink基本架构
Flink集群主要包括JobManager和TaskManager两个组件:
JobManager:负责调度Job并协调Task做checkpoint,类似于Storm的Nimbus。从Client处接收到Job和JAR包等资源后,会生成优化后的执行计划,并以Task的单元调度到各个TaskManager去执行。
TaskManager:在启动时设置好了槽位数(Slot),每个slot能启动一个Task,Task为线程。从JobManager处接收需要部署的Task,部署启动后,与自己的上游建立Netty连接,接收数据并处理。
Flink On Yarn结构
Flink on Yarn是由Client提交App到ResourceManager(RM)上,然后RM分配一个AppMaster负责运行Flink JobManager和Yarn AppMaster。AppMaster分配容器去运行Flink TaskManager。
Spark Streaming架构
Spark Streaming将流处理分成微批处理的作业,最后的处理引擎是Spark Job。Spark Streaming把实时输入数据流以时间片Δt(如1秒)为单位切分成块,Spark Streaming会把每块数据作为一个RDD,并使用RDD操作处理每一小块数据。每个块都会生成一个Spark Job处理,然后分批次提交job到集群中去运行,运行每个 job的过程和真正的spark 任务没有任何区别。
JobScheduler负责Job的调度,通过定时器每隔一段时间根据Dstream的依赖关系生一个一个DAG图。ReceiverTracker负责数据的接收、管理和分配。ReceiverSupervisor在启动Receiver的时候会启动Receiver,Receiver不断的接收数据,通过BlockGenerator将数据转换成Block。定时器会不断的把Block数据通过BlockManager或者WAL进行存储,数据存储之后ReceiverSupervisorlmpl会把存储后的数据的元数据Metadate汇报给ReceiverTracker。
Spark on Yarn
Spark on Yarn的Cluster模式中,Spark client向ResourceManager提交job请求,ResourceManager会分配一个AppMaster,Driver和AppMaster运行在AppMaster节点里。AppMaster把Receiver作为一个Task提交给Spark Executor节点,Receiver启动接受数据,生成数据块,并通知Spark Appmaster。AppMaster会根据数据块生成相应的Job,并把Job提交给空闲的Executor去执行。
对比Flink和Spark Streaming的Cluster模式可以发现,都是AM里面的组件(Flink是JM,spark streaming是Driver)承载了task的分配和调度,其他container承载了任务的执行(Flink是TM,spark streaming是Executor)。不同的是Spark Streaming每个批次都要与driver进行通信来进行重新调度,这样延迟性远低于Flink。
实时框架选择指南
在选择实时处理框架时,需要考虑以下因素:
- 流数据是否需要进行状态管理
- At-least-once或者Exectly-once消息投递模式是否有特殊要求
- 对于小型独立的项目,并且需要低延迟的场景,建议使用Storm
- 如果你的项目已经使用了Spark,并且秒级别的实时处理可以满足需求的话,建议使用Spark Streaming
- 要求消息投递语义为Exactly Once的场景;数据量较大,要求高吞吐低延迟的场景;需要进行状态管理或窗口统计的场景,建议使用Flink
Flink编程结构
Flink提供的API主要有DataStream和DataSet,他们都是不可变的数据集合,不可以增加删除中的元素。通过Source创建DataStream和DataSet。
- 获取运行时
// 流处理
StreamingExecutionEnvironment
// 批处理
ExecutionEnvironment
在创建运行时有:
createLocalEnvironment 和 createRemoteEnvironment
- 添加外部数据源
env.addSource(...)
- 定义算子
input.map{}
- 定义Sink
stats.addSink(...)
- 启动程序
env.execute()
Flink优化与调度策略
Flink的每一个Operator称为一个任务,Operator的每一个实例称为子任务,每一个任务在JVM线程中执行。可以将多个子任务链接成一个任务,减少上下文切换的开销,降低延迟。
source和算子map如果是one by one的关系,他们的数据交换可以通过缓存而不是网络通信。
TaskManager为控制执行任务的数量,将计算资源划分多个slot,每个slot独享计算资源,这种静态分配利于任务资源隔离。同一个任务可以共享一个slot,不同作业不可以。
Flink使用slot来隔离多个作业任务。
- 调度策略
env.addSource(...).setParallelism(4)
.map(...).setParallelism(4)
.reduce(...).setParallelism(3)
这里因为Source和Map并行度都是4采用直连方式,他们的数据通信采用缓存形式。所以一共需要两个TaskManager,source,Map一个,reduce一个,每个TaskManager要3个slot。
- 作业控制
JobManager将JobGraph部署ExecutionGraph:
- JobGraph由Operator和传输通道的数据缓存组成。Operator是计算图中的顶点JobVertex。
- ExecutionGraph由ExecutionVertex和中间结果的多个分区组成。
设置的并行度,可以让一个ExecJobVertex对应多个并行的ExecVertex实例。Flink通过状态机管理ExecGraph的作业执行进度。
Flink如何管理内存
Flink将对象序列化为固定数量的预先分配的内存段,而不是直接把对象放在堆内存上。Flink TaskManager是由几个内部组件组成的:actor系统(负责与Flink master协调)、IOManager(负责将数据溢出到磁盘并将其读取回来)、MemoryManager(负责协调内存使用)。
流处理API
数据源
env.readTextFile("/path")
env.readFile(inputFormat,"path")
env.socketTextStream("localhost", port,'\n')
env.fromElements(data: T*)
env.addSource(new FlinkKafkaConsumer08)
Sink
windowCounts.writeAsCsv()
windowCounts.print().setParallelism(1)
windowCounts.addSink()
windowCounts.writeToSocket()
时间
处理时间:取自Operator的机器系统时间
事件时间:由数据源产生
进入时间:被Source节点观察时的系统时间
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
env.setStreamTimeCharacteristic(TimeCharacteristic.IngestionTime)
env.setStreamTimeCharacteristic(TimeCharacteristic.ProcessingTime)
水印
如果数据源没有自己正确创建水印,程序必须自己生成水印来确保基于事件的时间窗口可以正常工作。DataStream提供了周期性水印、间歇式水印和递增式水印。
总结
本文详细介绍了Flink的基本原理、架构设计、编程模型以及与Spark Streaming的对比分析。对于想要深入了解Flink的读者来说,这篇文章具有较高的参考价值。但是需要注意的是,文章发布于2020年,距今已有5年时间,技术领域更新迭代较快,Flink的版本和特性可能已经发生了变化。因此,文章中的某些内容可能已经过时,需要谨慎对待。