分布式系列之分布式计算框架Flink深度解析

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


1、Flink概述

Apache Flink是一个框架和分布式处理引擎,用于在无边界和有边界数据流上进行有状态的计算。在现有的开源计算框架中,流式处理和批量处理会作为不同的应用类型,流处理一般需要低延迟和Extract-one保证,而批量处理需要支持高吞吐和高效处理,因此在实现上一般会采用不同的计算框架,比如流处理使用Storm而批量处理使用MapReduce计算引擎。而Flink能同时支持面向分布式流式处理和批量处理的应用场景,也就是作为流处理看待时输入数据流是无界的;批处理被作为一种特殊的流处理,只是它的输入数据流被定义为有界的。在运行Flink时,分别提供了流式和批处理的API,这两类API也是流处理和批处理应用的基础。

在这里插入图片描述

Flink官网:https://flink.apache.org/

核心理念:Apache Flink是为分布式、高性能、随时可用以及准确的流处理应用程序打造的开源流处理框架

1.1 Flink特性

Flink支持开发和运行多种不同种类的应用程序,它的主要特性包括:批流一体化、精密的状态管理、事件时间支持以及Exectly-one的状态一致性保障等。Flink不仅可以运行在包括YARN、Mesos、Kubernetes在内的多种资源管理框架上,还支持在裸机集群上独立部署。在启用高可用选项的情况下,它不存在单点失效问题。事实证明,Flink已经可以扩展到数千核心,其状态可以达到TB级别,且仍能保持高吞吐、低延迟的特性。对比其它类的开源计算框架,Flink流处理具有以下特性:

  • 支持高吞吐、低延迟、高性能的流处理,时延能达到ms级别
  • 支持带有事件时间的窗口(Window)操作
  • 支持有状态计算的Exactly-once语义
  • 支持高度灵活的窗口(time/count/session)Window操作,以及data-driven驱动
  • 支持具有BackPressure功能的持续流模型
  • 支持基于轻量级分布式快照(Snapshot)实现的容错
  • 一个运行时同时支持Batch on Streaming处理和Streaming处理
  • Flink在JVM内部实现了自己的内存管理
  • 支持迭代计算
  • 支持程序自动优化:避免特定情况下Shuffle、排序等昂贵操作,中间结果进行缓存
1.2 Flink应用场景

Flink适合以下应用场景:

  • Event-driven Applications:事件驱动应用
  • Data Analytics Applications:数据分析应用
  • Data Pipeline Applications:数据挖掘应用
1.2.1 事件驱动应用

事件驱动型应用是一类具有状态的应用,它从一个或多个事件流提取数据,并根据到来的事件触发计算、状态更新或其他外部动作。事件驱动型应用是在计算存储分离的传统应用基础上进化而来。在传统架构中,应用需要读写远程事务型数据库。相反,事件驱动型应用是基于状态化流处理来完成。在该设计中,数据和计算不会分离,应用只需访问本地(内存或磁盘)即可获取数据。系统容错性的实现依赖于定期向远程持久化存储写入checkpoint。下图描述了传统应用和事件驱动型应用架构的区别。

在这里插入图片描述

相比传统的应用架构,事件驱动型不需要实时访问后端的数据库,而定期向数据库持久化的checkpoint动作也可以异步、增量的方式实现,对数据库性能和架构稳定性依赖更低。不过事件驱动型应用会受制于底层流处理系统对时间和状态的把控能力,Flink提供了一系列丰富的状态操作原语,允许以Exactly-one的一致性语义合并海量规模(TB 级别)的状态数据。此外,Flink还支持事件时间和自由度极高的定制化窗口逻辑,而且它内置的ProcessFunction支持细粒度时间控制,方便实现一些高级业务逻辑。同时,Flink还拥有一个复杂事件处理(CEP)类库,可以用来检测数据流中的模式。

1.2.2 数据分析应用

数据分析任务需要从原始数据中提取有价值的信息和指标。传统的分析方式通常是利用批查询,或将事件记录下来并基于此有限数据集构建应用来完成。为了得到最新数据的分析结果,必须先将它们加入分析数据集并重新执行查询或运行应用,随后将结果写入存储系统或生成报告。

和传统模式下读取有限数据集不同,流式查询或应用会接入实时事件流,并随着事件消费持续产生和更新结果。这些结果数据可能会写入外部数据库系统或以内部状态的形式维护。图形化展示应用可以相应地从外部数据库读取数据或直接查询应用的内部状态。

在这里插入图片描述

Flink支持实时的流式分析和批量分析,其中内置了标准的SQL接口,将批量和流式的查询接口统一起来,无论是在记录事件的静态数据集上还是实时事件流上,相同SQL查询都会得到一致的结果。

1.2.3 数据挖掘应用

提取-转换-加载(ETL)是一种在存储系统之间进行数据转换和迁移的常用方法。ETL作业通常会周期性地触发,将数据从事务型数据库拷贝到分析型数据库或数据仓库。但数据管道是以持续流模式运行,而非周期性触发,因此它支持从一个不断生成数据的源头读取记录,并将它们以低延迟移动到终点。和周期性ETL作业相比,持续数据管道可以明显降低将数据移动到目的端的延迟。此外,由于它能够持续消费和发送数据,因此用途更广,支持用例更多。

在这里插入图片描述

Flink为多种数据存储系统(如:Kafka、Kinesis、Elasticsearch、JDBC数据库系统等)内置了连接器,利用Flink的SQL接口进行数据转换和增强操作。

1.3 有界和无界数据流

任何类型的数据都可以形成一种事件流,交易数据、传感器测量、机器日志、网站或移动应用程序上的用户交互记录,所有这些数据都形成一种流。数据可以被作为无界或者有界流来处理。

  • 无界流:有定义流的开始,但没有定义流的结束,它们会无休止地产生数据。无界流的数据必须持续处理,即数据被摄取后需要立刻处理。我们不能等到所有数据都到达再处理,因为输入是无限的,在任何时候输入都不会完成。处理无界数据通常要求以特定顺序摄取事件,例如事件发生的顺序,以便能够推断结果的完整性。
  • 有界流:有定义流的开始,也有定义流的结束。有界流可以在摄取所有数据后再进行计算。有界流所有数据可以被排序,所以并不需要有序摄取。有界流处理通常被称为批处理

在这里插入图片描述

2、Flink架构解析

2.1 Flink中数据流图
2.1.1 Flink程序结构

Flink程序的基本构建块是流和转换,流是数据记录流,而转换是将一个或多个流进行转换的操作。

在这里插入图片描述

Flink应用程序结构如上图所示,分为三部分:Source、Transformation和Sink

  • Source: 数据源,Flink在流处理和批处理上的source大概有4类:基于本地集合的source、基于文件的source、基于网络套接字的source、自定义的source。
  • Transformation:数据转换的各种操作,Map/FlatMap/Filter/KeyBy/Reduce/Fold/Aggregations/Window/WindowAll/Union/Windowjoin/Split/Select等
  • Sink:接收器,Flink将转换计算后的数据发送的地点,Flink常见的Sink大概有如下几类:写入文件、打印出来、写入socket、自定义的sink 。自定义的sink常见的有Apache kafka、RabbitMQ、MySQL、ElasticSearch、Apache Cassandra、Hadoop FileSystem等。
2.1.2 Flink并行数据流

Flink中数据流是核心数据的抽象,在Flink中使用DataStream表示数据流,在DataStream上定义了常见的数据处理操作API。Flink程序在执行的时候,会被映射成一个Streaming Dataflow,一个Streaming Dataflow是由一组Stream和Transformation Operator组成的。在启动时从一个或多个Source Operator开始,结束于一个或多个Sink Operator。

在这里插入图片描述

Flink在本质上是并行和分布式处理,当数据量超过单个节点的处理能力时,需要将一份数据切分到多个分区上,每个分区分布在一台服务器。上图展示了数据流从逻辑视图转换为物理执行图,map()算子在每个分区上都有一个算子子任务,算子子任务之间是相互独立的,每个算子子任务在不同的线程中独立执行,图中并行度为2,表示有2个并行执行的任务。

2.1.3 Task和算子链

Flink中的所有操作称为算子,客户端在提交任务的时候会对算子进行优化操作,能进行合并的算子会被合并为一个,合并后的算子称为算子链,实际上就是一个执行链,每个执行链会在TaskManager上一个独立的线程中执行。将算子链接成task能够减少线程间切换、缓冲的开销,并且减少延迟的同时增加整体吞吐量。

在这里插入图片描述

2.2 Flink集群架构

Flink集群是由一个JobManager和多个TaskManager组成的:Client用来提交任务给JobManager,JobManager分发任务给TaskManager去执行,然后TaskManager会以心跳的方式汇报任务状态。从架构图去看,JobManager很像Hadoop中的JobTracker,TaskManager也很像Hadoop中的TaskTracker。

在这里插入图片描述

2.2.1 Job Client

JobClient不是Flink程序执行的内部部分,它是任务执行的起点。主要职责如下:

  • 提交任务, 提交后可以结束进程, 也可以等待结果返回;
  • 负责接受用户的程序代码,然后创建数据流,将数据流提交给JobManager以便进一步执行;
  • 执行完成后,Job Client将结果返回给用户。
2.2.2 JobManager

JobManager是主进程(也称为作业管理器)协调和管理程序的执行。JobManager决定何时调度下一个task、对完成的task或执行失败做出反应、协调checkpoint、并且协调从失败中恢复等等。在整个集群中至少有一个Master,负责调度task,协调checkpoints和容灾。如果是高可用架构可以设置多个Master,但是只有一个是Leader,其它是standby。

JobManager进程由三个不同的组件组成:

  1. ResourceManager:负责Flink集群中的资源提供、回收、分配。它管理task slots,这是 Flink集群中资源调度的单位。Flink为不同的环境和资源提供者(例如YARN、Kubernetes 和standalone部署)实现了对应的ResourceManager。在standalone设置中,ResourceManager只能分配可用TaskManager的slots,而不能自行启动新的TaskManager。
  2. Dispatcher:Dispatcher 提供了一个 REST 接口,用来提交Flink应用程序执行,并为每个提交的作业启动一个新的JobMaster。它还运行Flink WebUI用来提供作业执行信息。
  3. JobMaster:负责管理单个JobGraph的执行。Flink 集群中可以同时运行多个作业,每个作业都有自己的JobMaster。
2.2.3 TaskManager

TaskManager从JobManager处接收需要部署的Task,并部署和启动任务, 接收上游的数据并处理,是实际负责执行计算的节点。

  • 集群中至少有一个TaskManager。一般一个Flink作业是分布在多个TaskManager上执行的,单个TaskManager上提供一定的slot
  • TaskManager是在JVM中的一个或多个线程中执行任务的工作节点。TaskManager启动后,相关的slot信息会被注册到ResourceManager中。当某个作业提交后,ResourceManager会将空闲的Slot提供给JobManager,JobManager再将这些空闲的slot资源进行分配
  • 任务执行的并行性由每个TaskManager上可用的Task Slots决定。每个任务代表分配TaskSlot的一组资源。例如,如果TaskManager有四个插槽,那么它将为每个插槽分配 25%的内存
2.2.4 TaskSlot

TaskSlot即任务槽,是TaskManager中资源调度的最小单位,task slot的数量表示并发处理task的数量。类似YARN当中的Container,用于资源的封装,但是在Flink中,taskSlot只负责封装内存的资源,不包含CPU的资源。

  • 每一个TaskManager中会包含3个TaskSlot,所以每一个TaskManager中最多能并发执行的任务是可控的,最多3个
  • TaskSlot有独占的内存资源,在一个TaskManager中可以运行不同的任务
  • 一个 task slot 中可以执行多个算子
  • Task Slot当中的 Task 就是任务执行的具体单元
  • 默认情况下,Flink 允许子任务共享Slot,即使它们是不同task的subtask,只要它们来自相同的 job。这种共享可以有更好的资源利用率。
2.2.5 Flink工作流程

在这里插入图片描述

Flink基本工作流程如上图所示:

  1. 用户编写应用程序代码,并通过Client客户端提交作业
  2. JobClient将作业提交给JobManager
  3. JobManager返回Client端作业提交成功
  4. JobManager在接收到作业后会向ResourceManager申请本次作业所需的资源,将作业中的逻辑视图转换为物理执行图,然后将计算任务分发部署到多个TaskManager上
  5. TaskManager接收到任务请求后,会启动一个线程开始执行。执行过程中,TaskManager会继续向JobManager报告作业执行的状态,比如开始执行、正在进行或已完成。
  6. 作业执行完成后,结果将发送回客户端(JobClient)
2.3 Task Slots和Resource

每个 worker(TaskManager)都是一个 JVM 进程,可以在单独的线程中执行一个或多个 subtask。为了控制一个TaskManager中接受多少个task,就有了所谓的 task slots(至少一个)。

  • 每个task slot代表TaskManager中资源的固定子集。例如,具有3个slot的TaskManager,会将其托管内存均分1/3用于每个slot。资源分配后意味着subtask不会与其他作业的subtask竞争托管内存,仅使用已经分配的资源。注意slot没有CPU隔离,只是针对内存进行了分配。
  • 通过调整task slot的数量,用户可以定义subtask如何互相隔离。如果Task slot数量为1,意味着每个task组都在单独的 JVM 中运行;如果有多个Task slots则意味着更多subtask共享同一JVM。同一JVM中的task共享TCP连接(通过多路复用)和心跳信息。它们还可以共享数据集和数据结构,从而减少了每个task的开销。

在这里插入图片描述

默认情况下,Flink允许subtask共享slot,即便它们是不同的task的subtask,只要是来自于同一作业即可。结果就是一个slot可以持有整个作业管道。允许slot共享有两个主要优点:

  • Flink集群所需的task slot和作业中使用的最大并行度恰好一样。无需计算程序中总共包含多少个task。
  • 容易获得更好的资源利用。如果没有slot共享,非密集subtask(source/map())将阻塞和密集型subtask(window)一样多的资源。通过slot共享,如下图所示并行度从2增加到6,可以充分利用分配的资源,同时确保繁重的subtask在TaskManager之间公平分配。

在这里插入图片描述

2.4 Flink组件栈

在这里插入图片描述

Flink具有分层架构,其中每个组件都是特定层的一部分,每层都建立在其他层之上,以实现清晰的抽象。Flink组件栈分为4层:Deploy层、Runtime层、API层和上层工具

1)Deploy部署层

Flink支持多种部署模式,包括local、cluster以及cloud部署

  • Local模式:JobManager和TaskManager等所有角色都运行在一个节点上,集群的节点只有一个,大都用于测试环境
  • Cluster模式:一般用于生产环境,可以是Standalone的独立集群,也可以是YARN或K8S集群
  • Cloud集群:Flink部署在云平台上

2)Runtime运行时层

Runtime层是Flink最底层也是最核心的组件,为Flink各类计算提供了实现。

3)API层

API层主要实现了流处理DataStream和批处理DataSet的API,DataStream和DataSet API是程序员可用于定义 Job 的接口。编译程序时,这些API会生成JobGraphs。

4)上层工具

Flink在流处理和批处理API之上,提供了丰富的工具,包括面向流处理的CEP、面向批处理的图计算、面向SQL的table API、 机器学习的FlinkML等。


参考资料:

  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内核原理与实现》,冯飞著

猜你喜欢

转载自blog.csdn.net/solihawk/article/details/126591947
今日推荐