FlumeJava: Easy, Efficient Data-Parallel Pipelines Abastrct

来源:互联网 发布:n个骰子的点数 java 编辑:程序博客网 时间:2024/05/24 15:43

本文主要是对《FlumeJava: Easy, Efficient Data-Parallel Pipelines》论文的概要总结

完整论文请参考 https://research.google.com/pubs/pub35650.html

部分内容做了简单概要总结,如有错误请指出


一、前言

二、FlumeJava库

2.1 核心抽象

2.1.1 核心类PCollection<T>

    FlumeJava库的核心类是 PCollection<T>,T是一个不可变的元素类型。一个PCollection<T>可以是有序的(一个sequence),或是无序的(一个collection)。由于collection的限制较少,collection和sequence相比在数据的集成和处理方面效率更高。
    PCollection<T>的来源多种多样,可以来源于Java内存中的Collection<T>,也可以从某几种格式的文件中读取,例如textfile和给定从二进制文件到Java对象T的解码方式的二进制文件。同时,由多个分片文件代表的Data set可以作为一个独立的逻辑PCollection被读入。

2.1.2 核心类PTtable<K, V>

    第二个核心类是PTable<K, V>,它代表了以K为key,V为value的不可变map。PTable<K, V>是PCollection<T>的一个子类,它的实际内容就是一些无序对的集合。一些FlumeJava的操作仅限于作用在PCollection<T>上,在Java中,PTable<K, V>我们选择定义一个子类来捕捉这种抽象,在其他语言中,PTable<K, V>可能更类似于PCollection<T>的同义词。

2.1.3 原子操作parallelDo

    操作PCollection的主要方式是在其之上触发一个数据并行运算。FlumeJava库仅定义了几个原子的数据并行操作,其他的操作都是在这些原子操作之上衍生而来。核心的数据并行运算原子操作是parallelDo(),支持接收多种不同的输入PCollection<T>,来产生一个新的输出PCollection<S>。这个操作的主要参数是DoFn<T, S>,它是一个类函数的对象,定义了如何从输入的PCollection<T>的每一个具体值映射到一个或是多个输出PCollection<S>。
    parellelDo()可以用来表达MapReduce框架中的map和reduce操作。由于它们可能会分发到不同的远程机器上来进行并行运算,parallelDo的函数参数DoFn不应该访问任何Java封闭程序域的全局变量。在理想情况下,它们应该是只作用于输入的纯函数操作。DoFn也可以对局部变量的状态进行管理,但是用户需要了解,并行执行的DoFn中不会共享状态。

2.1.4 原子操作groupByKey

    groupByKey()可以将一个PTable<K, V>类型的multi-map转换为一个PTable<K, Collection<V>>的uni-map,其中每一个key都映射到了一个包含该key下所有value的普通的无序Java集合。
    groupByKey()操作抓住了MapReuce中的shuffle step的本质。同样也支持通过变量指定key的排序规则。

2.1.5 原子操作combineValues

    combinValues接受输入PTable<K, Collection<V>>,和一个关联函数Vs,返回一个PTable<K, V>,在返回值中,每一个输入的collection值都被关联到一个唯一的输出值上。
    combinValues从语法上来说只是parallelDo()的一个特例,合并函数的结合性使得它能够由一个MapRduce中的combiner(作为每个mapper的一部分来运行)和一个MapReduce中的reduer(完成合并)来组合实现,这比在reducer中完成所有的combine效率更高

2.1.6 原子操作flatten

    flatten()函数接收一组PCollection<T>,返回一个包含所有输入的PCollection<T>中元素的单独的PCollection<T>。flatten不会直接复制输入数据,而是在输入基础上建立了一个逻辑视图。

2.2 衍生操作

    FlumeJava库包含许多其他关于PCollection的操作,但这些操作都是在原子操作基础上衍生出来的,和用户编写的帮助函数没有区别。下面选取其中几个例子简单分析

2.2.1 count

    count()函数读取一个PCollection<T>输入,返回一个PTable<T, Integer>,列举出来了所有输入PCollection中不同的元素和它们出现的次数。这个函数是根据parallelDo、groupByKey和combineValue衍生实现,和wordCount使用了同样的模式。

2.2.2 join

    join实现了一种通过共有的key来join两个或更多PTable。

2.2.3 top

    top函数基于parallelDo、groupByKey和combineValues基础上实现

2.3 延迟计算

    为了实现在下一章主要介绍的优化器,FlumeJava的并行计算是延迟执行的。每一个PCollection的内部状态都可以被“延迟”(还未计算)和“物化”(已计算完成)表示。一个延迟计算的PCollection会维护计算它的操作的引用。一个延迟计算操作,反过来,也会维护一个作为它的计算参数的PCollection的引用和作为它的结果的PCollection引用。当一个FlumeJava的操作,例如parallelDo被调用,它只会创建一个ParalleoDo的延迟计算对象并且返回一个新的延迟计算的PCollection引用。执行一系列FlumeJava操作的结果是一系列延迟执行的PCollection和操作的有向无环图;我们称这个图为执行计划。
    上图是一个简单的执行计划案例,这个pipeline接收了四个不同的输入源,产生两个输出。
    用户通过调用FlumeJava.run来实际出发一系列并行操作的计算。它会首先对执行计划进行优化,然后再去遍历优化后的执行计划中的每一个延迟操作,构建执行拓扑,并执行它们。当一个延迟操作执行时,它会将他的PCollection结果状态转换为“物化”状态。FlumeJava会在执行过程中管理生成的一些中间文件。

2.4 PObjects

    为了支持pipeline的执行中和执行之后对PCollections的检查(???为什么要检查),FlumeJava包含了一个PObject<T>类,包含了一个Java的泛型类T。和PCollection一样,PObject的状态可以有”延迟计算”或是”物化”,允许它们能在pipeline计算中以延迟计算结果存在。在一个pipeline计算完成后,一个物化后的PObject 对象中的值可以通过getValue方法提取出来。

三、优化器

    FlumeJava优化器将一个用户构造的、模块化的FlumeJava执行计划转换为可以高效执行的执行计划。优化器被写成一系列独立的图转换。

3.1 ParalleoDo融合

    最简单最直观的优化之一是ParalleoDo 【producer-consumer融合】,其本质上是函数组成或是循环融合。
    如果一个ParalleoDo操作执行了一个函数f,并且它的结果被另一个执行函数g的ParalleoDo操作消费,name这两个ParalleoDo操作就可以被一个单独的多输出ParalleoDo替代,它同时运行f和g函数。如果函数f的ParalleoDo不在被图中的其他ParalleoDo消费的话,融合就没有必要了。
    ParalleoDo的【sibling融合】运用在执行两个或多个读取同一PCollection输入的ParalleoDo情况下。他们会融合成为一个单独的multi-output的ParallelDo操作,该操作会通过单次输入计算出所有合并的操作结果。
    ParalleoDo的【producer-consumer融合】和【sibling融合】都可以作用于各种multi-output ParalleDo操作的节点上。下图展示了一种优化样例

   

3.2 MapShuffleCombineReduce(MSCR)操作

    FlumeJava的优化器核心是将ParallelDo、GroupByKey、CombineValues和Flattern等操作的结合转换为一个单独的MapReduce。为了帮助在这两个抽象概念中建立桥接关系,FlumeJava优化器中包含了一个中间层操作,MapShuffleCombineReduce(MSCR)操作。一个MSCR操作有M个输入信道(每个信道执行一个map操作),和R个输出信道(每个选择性地执行shuffle,combine和reduce操作)。每个输入信道m接收一个PCollection<Tm>作为输入,在输入上执行一个有R个输出的Paralleo “map”操作来产生R个PTable<Kr, Vr>类型的输出;输入信道可以选择想一个或是多个输出信道进行输出。每个输出信道r会将它的M个输入打平,选择性地执行GroupByKey,CombineValues和一个ParalleoDo reduce操作。
    MSCR支持多个reducer和combiner,来生成MapReduce,允许每个reducer产生多个输出,消除了一个reducer必须产生和input相同个数的输出的限制,从而来达成我们的优化目的。尽管它的功能是多种多样的,每个MSCR操作都是由单个MapReduce来实现的。下图是一个典型的MSCR操作

3.3 MSCR融合

3.4 整体优化策略

    优化器对执行计划执行一系列传递,总体目标是在最终优化计划中生成最少、最有效的MSCR操作:
  1. Flattern下推
  2. CombineValues提升
  3. 插入模块
  4. 合并ParalleoDo
  5. 合并MSCR

四、执行器

    执行计划被优化后,FlumeJava库就会运行它。目前,FlumeJava支持批处理:FlumeJava将执行计划里的操作转换为前向拓扑顺序,并且逆向遍历执行。相互独立的操作可以同时执行,通过操作支持能够完成数据并行计算的秉性任务。

    其中,最有意思的操作是执行MSCR。FlumeJava会决定操作应该被本地顺序执行,还是作为一个远程的并行MapReduce操作。由于启动一个远程并行job是有额外开销的,对于中型量级输入来说,本地并行的好处由于远程启动并行任务的开销。中型数据量级的数据通常用于本地开发和测试,可以使用IDE进行开发调试。

    在处理大数据量是,FlumeJava会启动一个远程并行的MapReduce。它通过监控输入数据量来确定输出数据量,以此为依据来动态确定并行作业的机器数量。用户可以通过实现函数来提供输出数据量级的预估。之后FlumeJava会通过动态监控输出数据量的反馈来动态确定最终输出数据量级,通过监控运行机器的CPU和IO来动态扩展。

    FlumeJava会对执行过程中产生的临时文件进行管理,它会动态删除pipeline计算中不再需要的临时文件。

    FlumeJava致力于让构建和运行pipeline和运行一个普通的Java程序一样简单。针对中型数据量级使用本地的顺序执行是其中一种方案。另外一种方法是将用户定义的DoFn函数中的输出自动指向System.out和System.err,例如针对debug的输入语句,从对应的远程MapReduce工作节点路由指向FlumeJava主程序的输出流。同样,对于远程MapReduce节点抛出的异常,FlumeJava会对其进行捕捉,发送回主程序,重新抛出。

    在开发大数据量的pipeline进行时,定位pipeline后期stage的bug、修复然和重新执行是比较耗费时间的,特别是根据小型数据子集可能无法定位到bug。为了支持这个循环流程执行,FlumeJava库支持缓存执行模式。在这个模式下,如果操作结果被保存在一个(内部或是用户可见)的文件中,并且FlumeJava判定操作结果没有发生变化时,FlumeJava首先会尝试重用之前执行的操作结果,而不是重新执行操作。一个操作的结果在同时满足下列条件下才会被认为是不变的:a. 操作输入不变;b. 操作的代码和状态未变。FlumeJava会执行自动可靠的分析去确定重用以前的结果是安全的,用户可以直接指定之前的结果进行重用。缓存执行可以让编译-运行-debug的历程更快。

    FlumeJava目前针对单次单pipeline实现了批处理评估策略。将来可能扩展到增量、streaming、或是连续的pipeline执行上。

    






原创粉丝点击