赞
踩
Apache Flink是一个面向数据流处理和批量数据处理的可分布式的开源计算框架,它基于同一个Flink流式执行模型(streaming execution model),能够支持流处理和批处理两种应用类型。由于流处理和批处理所提供的SLA(服务等级协议)是完全不相同, 流处理一般需要支持低延迟、Exactly-once保证,而批处理需要支持高吞吐、高效处理,所以在实现的时候通常是分别给出两套实现方法,或者通过一个独立的开源框架来实现其中每一种处理方案。比较典型的有:实现批处理的开源方案有MapReduce、Spark;实现流处理的开源方案有Storm;Spark的Streaming 其实本质上也是微批处理。
Flink在实现流处理和批处理时,与传统的一些方案完全不同,它从另一个视角看待流处理和批处理,将二者统一起来:Flink是完全支持流处理,也就是说作为流处理看待时输入数据流是无界的;批处理被作为一种特殊的流处理,只是它的输入数据流被定义为有界的。
用户实现的Flink程序是由Stream和Transformation这两个基本构建块组成,其中Stream是一个中间结果数据,而Transformation是一个操作,它对一个或多个输入Stream进行计算处理,输出一个或多个结果Stream。当一个Flink程序被执行的时候,它会被映射为Streaming Dataflow。一个Streaming Dataflow是由一组Stream和Transformation Operator组成,它类似于一个DAG图,在启动的时候从一个或多个Source Operator开始,结束于一个或多个Sink Operator。
下面是一个由Flink程序映射为Streaming Dataflow的示意图,如下所示:
上图中,FlinkKafkaConsumer是一个Source Operator,map、keyBy、timeWindow、apply是Transformation Operator,RollingSink是一个Sink Operator。
在Flink中,程序天生是并行和分布式的:一个Stream可以被分成多个Stream分区(Stream Partitions),一个Operator可以被分成多个Operator Subtask,每一个Operator Subtask是在不同的线程中独立执行的。一个Operator的并行度,等于Operator Subtask的个数,一个Stream的并行度总是等于生成它的Operator的并行度。有关Parallel Dataflow的实例,如下图所示:
上图Streaming Dataflow的并行视图中,展现了在两个Operator之间的Stream的两种模式:
流处理中的聚合操作(counts,sums等等)不同于批处理,因为数据流是无限,无法在其上应用聚合,所以通过限定窗口(window)的范围,来进行流的聚合操作。例如:5分钟的数据计数,或者计算100个元素的总和等等。
窗口可以由时间驱动 (every 30 seconds) 或者数据驱动(every 100 elements)。如:滚动窗口tumbling windows(无叠加),滑动窗口sliding windows(有叠加),以及会话窗口session windows(被无事件活动的间隔隔开)
三种不同的时间概念:
在流处理中,有些操作仅仅在某一时间针对单一事件(如事件转换map),有些操作需要记住多个事件的信息并进行处理(window operators),后者的这些操作称为有状态的操作。有状态的操作一般被维护在内置的key/value存储中。这些状态信息会跟数据流一起分区并且分布存储,并且可以通过有状态的数据操作来访问。因此这些key/value的状态信息仅在带key的数据流(通过keyBy() 函数处理过)中才能访问到。数据流按照key排列能保证所有的状态更新都是本地操作,保证一致性且无事务问题。同时这种排列方式使Flink能够透明的再分发状态信息和调整数据流分区。
Flink通过流回放和设置检查点的方式实现容错。一个checkpoint关联了输入流中的某个记录和相应状态和操作。数据流可以从checkpoint中进行恢复,并保证一致性(exactly-once 的处理语义)。 Checkpoint的间隔关系到执行是的容错性和恢复时间。
Flink把批处理作为特殊的流处理程序来执行,许多概念也都可以应用的批处理中,除了一些小的不同:
在Flink分布式执行环境中,会将多个运算子任务Operator Subtask串起来组成一个Operator Chain,实际上就是一个运算链。每个运算会在TaskManager上一个独立的线程中执行。将算子串连到任务中是一种很好的优化:它能减少线程间的数据交接和缓存,并且提高整体的吞吐,降低处理的时延。这种串联的操作,可以通过API来进行配置。如下图的数据流就有5个子任务,通过5个并行的线程来执行,所示:
Flink的运行时,由两种类型的进程组成:
从上图可以分析出Flink运行时的整体状态。 Flink的Driver程序会将代码逻辑构建成一个Program Dataflow(区分source、operator、sink等等),在通过Graph Builder构建DAG的Dataflow graph, 构建job,划分出task 和subtask等等。 Client 将job 提交到JobManager. Client 通过Actor System和JobManager 进行消息通讯,接收JobManager返回的状态更新和任务执行统计结果。 JobMangaer 按照Dataflow的Task 和Subtask的划分,将任务调度分配到各个TaskManager中进行执行。TaskManager会将内存抽象成多个TaskSlot,用于执行Task任务。JobManagers与TaskManagers之间的任务管理,Checkpoints的触发,任务状态,心跳等等消息处理都是通过ActorSystem。
每个Worker(Task Manager)是一个JVM进程,通常会在单独的线程里执行一个或者多个子任务。为了控制一个Worker能够接受多少个任务,会在Worker上抽象多个Task Slot (至少一个)。
每个Task Slot代表固定的资源子集。比如一个TaskManager有3个Slots,每个Slot能管理对这个Worker分配的资源的3分之1的内存。 对资源分槽,意味着Subtask不会同其他Subtasks竞争内存,同时可以预留一定的可用内存。目前Task Slot没有对CPU进行隔离,仅是针对内存。通过动态的调整task slots的个数,用户可以定义哪些子任务可以相互隔离。只有一个slot的TaskManager意味着每个任务组运行在一个单独JVM中。 在拥有多个slot的TaskManager上,subtask共用JVM,可以共用TCP连接和心跳消息,同时可以共用一些数据集和数据结构,从而减小任务的开销。
默认情况下,Flink允许子任务共享slots,即便它们是不同任务的子任务,只要属于同一个job。这样的结果就是一个slot会负责一个job的整个pipeline。共用slot有两个好处:
数据的KV索引信息存储在设定的状态后端的存储中。一种是内存中的Hash map,另一种是存在Rocksdb(KV存储)中。另外,状态后端还是实现了在时间点上对KV状态的快照,并作为Checkpoint的一部分存储起来。
通过Data Stream AP编写的程序可以从一个保存点重新开始执行。即便你更新了你的程序和Flink集群都不会有状态数据丢失。保存点是手动触发的,触发时会将它写入状态后端。Savepoints的实现也是依赖Checkpoint的机制。Flink 程序在执行中会周期性的在worker 节点上进行快照并生成Checkpoint。因为任务恢复的时候只需要最后一个完成的Checkpoint的,所以旧有的Checkpoint会在新的Checkpoint完成时被丢弃。Savepoints和周期性的Checkpoint非常的类似,只是有两个重要的不同。一个是由用户触发,而且不会随着新的Checkpoint生成而被丢弃。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。