Spark源码之withScope方法的理解

最近在学习spark时候老是遇到withScope函数的运用,看的我也是一脸懵逼,花了好长的时间也是终于弄懂了,憋说话,看屏幕,直接上代码:

而我们经常见得调用就是如下格式的:

def sortByKey(ascending: Boolean = true, numPartitions: Int = self.partitions.length)
      : RDD[(K, V)] = self.withScope
  {
    val part = new RangePartitioner(numPartitions, self, ascending)
    new ShuffledRDD[K, V, V](self, part)
      .setKeyOrdering(if (ascending) ordering else ordering.reverse)
  }

这个withScope方法如下:

//上面{}内容就是传参给body
 private[spark] def withScope[U](body: => U): U = RDDOperationScope.withScope[U](sc)(body)
 

从形式上看,上面代码的花括号{}里面的几行代码就是一个匿名无参函数,直接传给了(body:=>U),而实际调用的是:
RDDOperationScope.withScopeU(body)这个方法。
这里涉及到一个知识点:柯里化
我普及一下柯里化知识点:就是把接收多个参数的函数变为接收一个单一参数的函数,将第一个参数运算得到的值传给下一个函数一起运算,从而得到一个新的函数个过程,讲的有点复杂,看例子:
例如:

def add(x:Int,y:Int)=x+y

柯里化后:

def add(x:Int)(y:Int)=x+y

柯里化的函数你可以这么理解:def add=(x:Int)=>(y:Int)=>x+y

具体可以参考如下的文章去理解:https://blog.csdn.net/onwingsofsong/article/details/77822920

OK,我们回到主题:RDDOperationScope.withScopeU(body)的中就是用到了柯里化的技术,他的作用就是把公共部分(函数体)抽出来封装成方法(这里的公共部分我的理解就是sc,也就是sparkCont), 把非公共部分通过函数值传进来(这里就是body的内容了),为什么要这么做呢:
withScope是最近的发现版中新增加的一个模块,它是用来做DAG可视化的(DAG visualization on SparkUI)
以前的sparkUI中只有stage的执行情况,也就是说我们不可以看到上个RDD到下个RDD的具体信息。于是为了在
sparkUI中能展示更多的信息。所以把所有创建的RDD的方法都包裹起来,同时用RDDOperationScope 记录 RDD 的操作历史和关联,就能达成目标。下图所示就是一个DAG图片了
在这里插入图片描述

我参考了如下文章:
原文链接:https://blog.csdn.net/lhui798/article/details/51860969

OK,现在我们知道了withScope的目的和用到了技术了,下面技术分析该函数用到的源码:
也就是RDDOperationScope.withScopeU(body)里面的源码:

private[spark] def withScope[T](
      sc: SparkContext,
      //是否允许嵌套
      allowNesting: Boolean = false)(body: => T): T = {
      ///设置跟踪堆的轨迹的scope名字
    val ourMethodName = "withScope"
    //下面这段代码其实就是将调用的方法名选出来,getStackTrace我就不说了,获取栈轨
    val callerMethodName = Thread.currentThread.getStackTrace()
    //dropWhile就是移除前几个匹配断言函数的元素,意思就是如果该线程下的方法名不是ourMethodName(withScope)那就移除
      .dropWhile(_.getMethodName != ourMethodName)
     **//再从剩下的元素中选出方法名不是ourMethodName(withScope),老实说我也不明白为什么多此一举?**
      .find(_.getMethodName != ourMethodName)
      .map(_.getMethodName)
      .getOrElse {
        // Log a warning just in case, but this should almost certainly never happen
        logWarning("No valid method name for this RDD operation scope!")
        "N/A"
      }
     //实际调用的是这个方法:
    withScope[T](sc, callerMethodName, allowNesting, ignoreParent = false)(body)
  }

withScope[T](sc, callerMethodName, allowNesting, ignoreParent = false)(body)函数的源码如下:欢迎补充说明,并告诉我哈

private[spark] def withScope[T](
      sc: SparkContext,
      name: String,
      allowNesting: Boolean,
      ignoreParent: Boolean)(body: => T): T = {
    //先保存老的scope,之后恢复它  
    val scopeKey = SparkContext.RDD_SCOPE_KEY
    val noOverrideKey = SparkContext.RDD_SCOPE_NO_OVERRIDE_KEY
    val oldScopeJson = sc.getLocalProperty(scopeKey)
    val oldScope = Option(oldScopeJson).map(RDDOperationScope.fromJson)
    val oldNoOverride = sc.getLocalProperty(noOverrideKey)
    try {
      if (ignoreParent) {//当ignorePatent设置为true的时候,那么回忽略之前的全部设置和scope  ,从我们自己的根scope重新开始
        // Ignore all parent settings and scopes and start afresh with our own root scope
        sc.setLocalProperty(scopeKey, new RDDOperationScope(name).toJson)
      } else if (sc.getLocalProperty(noOverrideKey) == null) {
        // 否则,仅在上级调用者允许时才设置scope
        sc.setLocalProperty(scopeKey, new RDDOperationScope(name, oldScope).toJson)
      }
      //可选地,不允许子body覆盖我们的scope
      if (!allowNesting) {
        sc.setLocalProperty(noOverrideKey, "true")
      }
      body
    } finally {
      // 记住,在退出之前要保存任何修改过的状态
      sc.setLocalProperty(scopeKey, oldScopeJson)
      sc.setLocalProperty(noOverrideKey, oldNoOverride)
    }
  }
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值