1.ApacheFlink的命脉“Lifeblood”即生命血脉,常指极其重要的事物。该系列的第一篇文章,第一篇第一段不讲ApacheFlink的历史,不讲ApacheFlink的架构,也不讲ApacheFlink的特点。一句话说说ApacheFlink的命脉是什么?我的回答是:ApacheFlink是基于“batchisaspecialcaseofflow”的认知而设计的。2、唯快不破我们经常听到“天下武功,唯快不破”,大概意思是“任何一种武术招式都可以拆,唯有速度快,快到让对手来不及反应,你就被对方KO了,对方没有机会拆招,所以唯有动作要快,不能破。那么这和ApacheFlink有什么关系呢?ApacheFlink是一个NativeStreaming(纯流式)计算引擎。在实时计算场景中,最关心的是“快”,即“低延迟”。就ApacheSpark和ApacheFlink这两个最火的流计算引擎而言,谁最终会成为No.1?从“低延迟”的角度来看,Spark是一种MicroBatching(微批处理)模式,***Spark的延迟可以达到0.5~2秒左右。Flink是NativeStreaming(纯流式)模式,最大延迟可以达到微秒级。显然,出道较晚的ApacheFlink后来居上。那么为什么ApacheFlink可以这么“快”呢?根本原因在于ApacheFlink最初的设计理念是相信“batch是stream的特例”。整个系统采用NativeStreaming设计,每条数据都可以触发计算。相对于需要时间积累数据的MicroBatching模式,在架构上已经占据绝对优势。那么为什么流计算会有两种计算模式呢?根本原因在于对流计算的认知不同,这是两种不同认知的产物:“流是批的特例”和“批是流的特例”。1.Micro-Batching模式Micro-Batching计算模式认为“流是批处理的特例”。流式计算就是连续计算连续的批次。如果batch足够小,就会有足够小的延迟,一定程度上满足。99%实时计算场景。那为什么那1%做不到呢?这就是建筑的魅力。Micro-Batching模式架构的实现中,有一个自然流数据流入系统进行批处理的过程,一定程度上增加了延迟。.具体示意图如下:显然,Micro-Batching模式有其与生俱来的低延迟瓶颈,但万物的存在都有两个方面。在大数据计算的发展史上,MapReduceonHadoop本来就是一个优秀的批处理模式计算框架。Micro-Batching可以借鉴很多成熟的设计和实现实践。2.NativeStreaming模式NativeStreaming计算模式认为“batch是stream的特例”,这种认知更适合stream的概念,比如一些监控消息流,数据库操作的binlog,实时支付交易信息和其他自然流数据一次一块地流动。NativeStreaming的计算方式是每一条数据到达时都进行计算。这种计算方式更自然,延迟性能更低。具体示意图如下:可见,NativeStreaming模式占据了流计算领域“低延迟”的核心竞争力。当然,NativeStreaming模式的实现框架是有历史先例的。第一个实现NativeStreaming模式的流计算框架是第一个吃螃蟹的人,需要面对更多的挑战我们会在后面的章节慢慢介绍。当然,NativeStreaming模式的框架可以轻松实现Micro-Batching和Batching模式的计算。3.丰富的部署模式ApacheFlink根据不同需求支持Local、Cluster和Cloud三种部署模式。同时,ApacheFlink可以在部署上与其他成熟的生态产品充分融合。例如,YARN(YetAnotherResourceNegotiator)/Mesos集成用于资源管理,并且可以在Cloud部署模式下与GCE(GoogleComputeEngine)、EC2(ElasticComputeCloud)集成。1.Local模式这种模式下,ApacheFlink整体运行在SingleJVM中,用于开发和学习,也可以安装在很多终端设备上。2.集群模式这种模式是典型的生产集群模式。ApacheFlink可以独立部署,也可以与其他资源管理系统(如YARN)集成。这种部署模式是典型的Master/Slave模式。我们以StandaloneCluster模式为例如下:JM(JobManager)为Master,TM(TaskManager)为Slave。这种Master/Slave模式的一个典型问题就是SPOF(单点故障),SPOF是如何解决的呢?ApacheFlink还提供了HA(HighAvailability)方案,即提供多个Master。任何时候,总有一个JM在服务,N(N>=1)个JM候选,然后解决SPOF问题,示意图如下:在实际生产环境中,我们会配置HA方案。目前阿里巴巴也在使用基于YARNCluster的HA方案。3、云模式这种模式主要是与成熟的云产品集成。ApacheFlink官网介绍了Google的GCE参考和Amazon的EC2参考。在阿里巴巴,我们也可以将ApacheFlink部署到阿里巴巴的ECS(ElasticComputeService)上。四、完善的容错机制1、什么是容错?FaultTolerance是指容错,当发生故障时可以自动检测到,系统可以自动恢复正常运行。当出现某些指定的网络故障、硬件故障、软件错误时,系统仍然可以执行一组指定的程序,或者程序不会因为系统故障而终止,执行结果也不是系统故障造成的.计算错误。2、容错处理模式在分布式系统中,由于单个进程或节点的故障,可能导致整个Job失败。容错机制不仅要保证系统在意外情况下能够“运行”,而且要“正确运行”,即数据能够按照预期的处理方式进行处理,以保证计算结果的正确性。计算结果的正确性取决于系统对每个计算数据的处理机制。一般有以下三种处理机制:AtMostOnce:最多消费一次。这种处理机制可能会导致数据丢失。AtLeastOnce:至少消费一次,这种处理机制数据不会丢失,但可以重复消费。ExactlyOnce:无论在什么情况下,数据只会被消费一次。这种机制是对数据准确性的绝对要求。必须用于金融支付、银行账户等领域。3.ApacheFlink的容错机制ApacheFlink的工作涉及三个部分,外部数据源(ExternalInput)、Flink内部数据处理(FlinkDataFlow)和外部输出(ExternalOutput)。示意图如下:ApacheFlink目前支持两种数据容错机制:AtLeastOnceExactlyOnce其中,ExactlyOnce是最严格的容错机制。这种模式要求每条数据都必须处理并且只处理一次。那么对于这种严格的容错机制,一个完整的FlinkJob容错必须要结合三部分进行联合处理,才能实现End-to-End的容错。根据上图,我们考虑三种场景:场景一:Flink的SourceOperator在读取Kafla中pos=2000的数据时,由于某种原因导致机器崩溃。此时,Flink框架会分配一个新的节点继续读取Kafla数据。那么新的处理节点如何处理才能保证数据处理只被处理一次呢?场景二:FlinkDataFlow内部的一个节点,如果上图中的agg()节点出现问题,恢复后如何处理,让map()流出的数据只处理一次?场景三:FlinkSinkOperator在写入Kafka的过程中,自身节点出现问题。恢复后如何处理,才能保证计算结果被写入且只写入一次?4.系统内部容错ApacheFlink使用Checkpointing机制来处理容错,Checkpointing的理论基础在Stephan在LightweightAsynchronousSnapshotsforDistributedDataflows中有详细的描述,来源于Determining-Global-States-of-a-Distributed-System论文由K.MANICHANDY和LESLIELAMPORT发表。ApacheFlink基于Checkpointing机制对FlinkDataFlow实现了AtLeastOnce和ExactlyOnce两种容错处理模式。ApacheFlinkCheckpointing的内部实现会使用Barriers和StateBackend等后续章节详细介绍的技术来处理数据作为Markers。ApacheFlink会使用barrier来标记和分割整个流程,如下示意图:这样,ApacheFlink的每个operator都会记录下当前成功处理的Checkpoint。如果发生错误,它将继续处理从最后一个成功的Checkpoint开始的后续数据。例如,SoruceOperator会实时记录读取外部数据源的位置到Checkpoint中,失败时会从Checkpoint中读取成功的位置,继续精准消费数据。每个算子都会在Checkpoint中记录自己恢复所需要的数据,比如流量的原始数据和中间计算结果等,恢复时从Checkpoint中读取并继续处理流量数据。5.ExternalSource容错ApacheFlink需要外部Source的支持才能实现End-to-EndExactlyOnce。比如我们上面提到ApacheFlink的Checkpointing机制会在Source节点上记录读取的Position,这就需要外部提供ReadPosition数据,支持基于Position的数据读取。6.ExternalSinkFaultToleranceApacheFlink实现End-to-EndExactlyOnce难度相对较大。如上文第三种场景所述,当SinkOperator节点宕机恢复时,根据ApacheFlink内部系统容错exactlyonce的保证,系统会回滚到上一次成功的Checkpoint继续写入,但是之后最后一个成功的Checkpoint,在当前Checkpoint完成之前,一些新数据已经写入Kafka。ApacheFlink从上次成功的Checkpoint开始继续向Kafka写入,导致Kafka再次从SinkOperator接收到相同的数据,这就破坏了End-to-EndExactlyOnce语义(重复写入变成了AtLeastOnce),如果要解决这个问题,ApacheFlink使用两阶段提交(two-phasecommit)进行处理。本质上,SinkOperator需要感知整体Checkpoint的完成,并在整体Checkpoint完成后将计算结果写入Kafka。5、流与批统一计算引擎批与流是两种不同的数据处理模式。比如ApacheStorm只支持流式处理数据,ApacheSpark只支持MicroBatching方式处理数据。那么ApacheFlink是如何同时支持流处理模式和批处理模式的呢?1.统一数据传输层在章节开头我们介绍了ApacheFlink的“命脉”是基于“batch是stream的特例”来进行engine系统的设计,设计为“Native”Streaming”模式进行数据处理。那么ApacheFLink将以批处理方式执行的任务视为流处理任务的特例,但是批处理是有界的(有限个元素)在数据上。ApacheFlink在网络传输层面有两种数据传输模式:PIPELINED模式——即一条数据处理完后,立即传输到下一个节点进行处理。BATCH模式——即一条数据处理完成后,不会立即传送给下一个节点处理,而是写入缓存区。如果缓存满了,会持久化到本地硬盘。***当所有的数据都处理完后,再将数据传输到下一个节点进行处理。对于批处理任务,也可以使用PIPELINED模式。比如我要做计数统计,使用PIPELINED方式可以获得更好的执行性能。只有在特殊情况下,比如SortMergeJoin,当我们需要全局数据排序时,才需要BATCH模式。大多数情况下,流和批可以采用统一的传输策略。只有在特殊情况下,批处理才被视为流的特例,并继续进行特殊处理。2.统一任务调度层ApacheFlink与上游和批量任务调度共享统一的资源和任务调度机制(后续章节会详细介绍)。3、统一的用户API层ApacheFlink基于DataStremAPI和DataSetAPI,为用户提供统一的上层流批TableAPI和SQL,以及流批高度统一的语法和语义。(DataStremAPI和DataSetAPI分别对流和批进行抽象,不够优雅,阿里巴巴内部统一抽象)。4.求同存异。ApacheFlink是一个统一的流式和批式计算引擎。这并不意味着流和批处理任务遵循统一的代码路径。底层具体算子的实现也有自己的处理。具体的功能会根据不同的特性来区分。比如batch没有Checkpoint机制,SortMergeJoin无法对stream进行。6.ApacheFlink架构1.组件栈在上面的内容中我们介绍了ApacheFlink的很多不同的组件。我们来看一下整个图景,如下:TableAPI和SQL是建立在DataSetAPI和DataStreamAPI的基础上的,那么TableAPI和SQL是如何转化为DataStream和DataSet的呢?2、从TableAPI&SQL到DataStream&DataSet的架构TableAPI&SQL经过Calcite的优化??,最终会转化为DataStream和DataSet。成一个数据集。3.ANSI-SQL支持ApacheFlink使用ANSI-SQL作为用户的统一开发语言,是因为SQL有非常明显的优势,如下:声明式——用户只需要表达自己想要的,而不用关心如何计算.Optimized——查询优化器可以为用户的SQL生成最佳的执行计划,以获得最佳的查询性能。易懂——SQL语言为不同领域的人所熟知,使用SQL作为跨团队开发语言可以大大提高效率。稳定——SQL是一门有几十年历史的语言,是一门非常稳定的语言,几乎没有什么变化。Unify-ApacheFlink在引擎上统一流和批,同时使用ANSI-SQL在语法和语义层面进行统一。4、最新的扩展优化机制ApacheFlink使用ApacheCalcite对SQL进行分析和优化。ApacheCalcite使用开源查询引擎Calcite,并实现了两套Planners:HepPlanner-RBO(RuleBaseOptimize)模式,基于规则的优化。VolcanoPlanner-是CBO(CostBaseOptimize)模式,基于成本的优化。FlinkSQL会使用Calcite进行分析和优化,最终转化为底层的DataStream和Dataset。上图中,Batch规则和Stream规则可以根据优化需要进行优化。7、丰富的类库和算子ApacheFlink优秀的架构就像摩天大楼的地基,为ApacheFlink的持久生命力奠定了良好的基础,为打造ApacheFlink丰富的功能生态留下了广阔的空间。1.类库CEP——复杂事件处理类库,核心是状态机,广泛应用于事件驱动的监控预警业务场景。ML——机器学习类库,机器学习主要是识别数据中的关系、趋势和模式,一般用于预测业务场景。GELLY-图形计算类库。图计算更多的是关于边和点的概念,一般用于解决有网络关系的业务场景。2.OperatorsApacheFlink提供了丰富的功能算子。对于数据流处理,可以分为单流处理(一个数据源)和多流处理(多个数据源)。3、多流操作UNION——将多个字段类型一致的数据流合并为一个数据流,如下图:JOIN——将多个数据流(数据类型可以不一致)合并为一个数据流,如下图:如上通过UION和JOIN,我们可以将多个流变成一个流。ApacheFlink在单个流上提供了更多的操作符。4.单流操作将多个流变成单流后,我们根据数据输入输出的不同分类如下:上表简单的对单流上的操作进行了分类,除了过滤,排序、开窗等操作,我们会在后面的章节中一一介绍。4.存在的问题ApacheFlink目前的架构还有很大的优化空间。比如上面提到的DataStreamAPI和DataSetAPI,其实就是流批不一致在API层面的体现。同时,如果看具体的实现,会发现DataStreamAPI会生成一个Transformation树。然后生成StreamGraph,最后生成JobGraph,底层对应StreamTask,但是DataSetAPI会形成Operator树,flink-optimize模块会优化BatchPlan,形成OptimizedPlan后形成JobGraph,最终形成BatchTask。具体说明如下:在这种情况下,其实DataStreamAPI到Runtime和DataSetAPI到Runtime的实现并没有得到最大程度的统一和复用。至此,阿里对ApacheFlink的增强在架构和实现上都得到了进一步的优化。八、阿里巴巴针对ApacheFlink的架构增强1.组件栈阿里巴巴针对ApacheFlink做了很多架构优化。下面的架构是我们一直在努力的方向。大多数功能仍在不断开发中。详情如下:我们发现上面的架构比较庞大。变化是:QueryProcessing-我们增加了QueryProcessing层,它进行统一的流和批查询优化和底层算子的转换。DAGAPI-我们在运行时统一了抽象的API接口,在API层统一了流和批。2.从TableAPI&SQL到Runtime的架构ApacheFlink的执行层是统一的流批设计。我们在API和算子设计上尽量做到流批共享。在TableAPI和SQL层,流式任务和批式任务最终统一转化为一个。的底层实现。示意图如下:这一层的核心变化是batch最终会生成一个StreamGraph,执行层运行StreamTask。9.特别是后面的章节会重点介绍阿里巴巴对ApacheFlink的增强,介绍函数式算子。章节中分享的功能可能暂时不会开源,但这些内容稍后会由阿里巴巴分享给社区,需要大家耐心等待。十、总结本文简单介绍“批处理是流的特例”的设计理念是ApacheFlink的“命脉”,决定了ApacheFlink的运行模式是纯流,真正的“低”-时间计算场景。在“延迟”要求上,与MicroBatching模式相比,在架构上具有绝对优势。同时简单介绍了ApacheFlink的部署模式、容错处理、引擎统一和ApacheFlink的架构。***分享给大家阿里巴巴对ApacheFlink的增强架构和对开源ApacheFlink的优化。本文不详细展开具体技术。只要对ApacheFlink有一个初步的了解,知道阿里巴巴对ApacheFlink的架构进行了优化,增加了很多功能,就足够了。至于ApacheFlink的具体技术细节和实现原理,以及阿里巴巴对ApacheFlink做了哪些架构优化和新增功能,将在后续章节进行介绍!#关于点赞和评论本系列文章难免有很多瑕疵和不足。真诚希望读者对有收获的章节给予好评和鼓励。先谢谢大家对不足章节的反馈和建议!作者孙金城,昵称金珠,目前就职于阿里巴巴。2015年开始投入阿里巴巴基于ApacheFlink的计算平台Blink的设计和开发。【本文为专栏作家“金竹”原创稿件,转载请联系原作者】点此阅读更多该作者好文
