Spark源码解析(一)

来源:互联网 发布:银联数据硕士待遇 编辑:程序博客网 时间:2024/09/21 11:17

RDD之getNarrowAncestors内部方法分析

最近开始spark的源码攻关,其实看源码一直是我最怕的东西,因为太多、太杂、太深导致不能够很好的把我脉络导致每次最后都放弃。有人跟我说看源码可以阶段性一个方法一个方法的去学习,去看,每天积累一点总会成功,那么今天开始我的第一天spark源码分析。

我这里从spark最基本的RDD中的方法说起,我感觉这样会更容易一些。同时我只对其中感觉比较重要的方法进行一些讲解。今天我主要讲一下getNarrowAncestors方法,也就是所谓的获取窄依赖的父RDD的方法,这个方法是一个内部方法,方法整体如下:


/** * Return the ancestors of the given RDD that are related to it only through a sequence of * narrow dependencies. This traverses the given RDD's dependency tree using DFS, but maintains * no ordering on the RDDs returned. */private[spark] def getNarrowAncestors: Seq[RDD[_]] = {  val ancestors = new mutable.HashSet[RDD[_]]  def visit(rdd: RDD[_]) {    val narrowDependencies = rdd.dependencies.filter(_.isInstanceOf[NarrowDependency[_]])    val narrowParents = narrowDependencies.map(_.rdd)    val narrowParentsNotVisited = narrowParents.filterNot(ancestors.contains)    narrowParentsNotVisited.foreach { parent =>      ancestors.add(parent)      visit(parent)    }  }  visit(this)  // In case there is a cycle, do not include the root itself  ancestors.filterNot(_ == this).toSeq}

方法的返回值为Seq类型,里面包含RDD,这不难理解,一个RDD他是窄依赖,那么他有一个父RDD,但是你不能保证他的父RDD没有依赖,所以返回结果为一个序列,里面装有很多的RDD。然后一开始我们看到一个HashSet,他的里面装有RDD(不过目前是空的),为什么用HashSet?好问题,因为它里面没有重复,我们不需要获取一个含有重复的序列(就像别人问你家里都有谁,你不会说两次爸爸一样)。下面定义了一个方法visit,首先这里很有意思,因为我们在一个方法里面定义了另外一个方法,所以你不得不佩服scala的灵活性。

这个visit方法需要传入一个RDD作为参数。然后我们进入方法第一行,rdd调用他的dependencies方法,以下是dependencies方法:


/** * Get the list of dependencies of this RDD, taking into account whether the * RDD is checkpointed or not. */final def dependencies: Seq[Dependency[_]] = {  checkpointRDD.map(r => List(new OneToOneDependency(r))).getOrElse {    if (dependencies_ == null) {      dependencies_ = getDependencies    }    dependencies_  }}

这个方法同样返回一个Seq里面是Dependecy的类型,Dependency类型是一个抽象类,很简单,整体如下:


abstract class Dependency[T] extends Serializable {  def rdd: RDD[T]}

只有一个RDD,那么回到上面dependencies方法中,第一行,我们查看checkpointRDD。可能有很多人不知道这是什么,那么请查看如下代码:


/** An Option holding our checkpoint RDD, if we are checkpointed */private def checkpointRDD: Option[CheckpointRDD[T]] = checkpointData.flatMap(_.checkpointRDD)

可以看到,checkpointRDD是一个Option类型里面包含的RDD是我们已经checkpointed的。我的理解是,这句可以理解为一个过滤过程。回到上面的dependencies方法中,我们看到checkpointRDD对其内的每个元素(也就是CheckpointRDD类型的RDD)执行方法OneToOneDependency(),代码如下:


/** * :: DeveloperApi :: * Represents a one-to-one dependency between partitions of the parent and child RDDs. */@DeveloperApiclass OneToOneDependency[T](rdd: RDD[T]) extends NarrowDependency[T](rdd) {  override def getParents(partitionId: Int): List[Int] = List(partitionId)}

这里为不引起混乱,不在进入更深的方法,这个方法的含义是获取所有的子RDD与父RDD的对应关系为1对1的父RDD的分区号。回到denpendencies方法中,我们看到下面有个方法getDependencies顾名思义,获取所有的依赖(此处方法不在深入),此时dependencies方法返回结果,返回一开始的visit方法中,为了方便查看,我再打印出来一遍:


def visit(rdd: RDD[_]) {  val narrowDependencies = rdd.dependencies.filter(_.isInstanceOf[NarrowDependency[_]])  val narrowParents = narrowDependencies.map(_.rdd)  val narrowParentsNotVisited = narrowParents.filterNot(ancestors.contains)  narrowParentsNotVisited.foreach { parent =>    ancestors.add(parent)    visit(parent)  }}

获取到值进行filter过滤,过滤的条件是查看里面的RDD是否为NarrowDependency类型。然后我们将过滤后的值保存变量名字为:narrowDependencies,然后将依赖中的rdd取出保存变量名字为narrowParents,也就是所谓的父RDD。然后下面几段代码的目的是把父RDD放入到我们一开始创建的HashSet中,重复的去掉,没有的加入。

再回到getNarrowAncestors方法中,我们看到下面它调用了visit方法,把本身作为参数传入。最后那一句是为了过滤出父类中又包含自身RDD这种循环而存在的,把过滤后的转化为Seq作为结果传出。

0 0