sparkCore之序列化闭包检测

(1)闭包检测
如果算子里面的代码访问了算子外面的代码,那么一定会将算子外的代码传输到算子内执行,那么一定会产生闭包。如果
这个外面的数据是对象,那么一定要做序列化,因为这个数据需要在网络中传输。
如果闭包使用的变量不能序列化,那么根本就不会执行作业,不会走到RunJob()
闭包形成的情况:
1、匿名函数用到了外部的变量或者对象,就形成了闭包
2、scala中的类传入参数,参数会在这个类的内部形成变量供自己使用
所以下面Text在网络中传输的话也需要做序列化
 class Text(aaa :String){
    def bbb(): Unit ={
      println(aaa)
    }
  }
示例:
package sparkcore.bibao

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

object Spark_RDD_Operate_Action {
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("bibao")
    val sc = new SparkContext(conf)
    val user = new User
    // 情况一: Task not serializable
//    val rdd: RDD[Int] = sc.makeRDD(List(1,2,3,4,5),2)
//    rdd.foreach(num=>{
//      println(user.age+num)
//    })

    val rdd2: RDD[Int] = sc.makeRDD(List(),2)
    rdd2.foreach(num=>{
      println(user.age+num)
    })
    sc.stop()
  }
  //情况一:使用rdd的有数据集合, Task not serializable
  // 使用rdd2的空集合执行, Task not serializable,但是这种情况与使用rdd不同,rdd2根本没有执行到runJob,
  //因为 foreeach算子内的代码是在EXECUTOR执行,使用了匿名函数,匿名函数中包含user类,匿名函数在使用时包含了算子外的对象,形成了闭包。
  //spark在执行作业前会先对闭包中的类做序列化检查,如果对象没有序列化不能在网络传输,都不会启动作业执行(若先启动发现对象不能序列化,程序执行不了,会造成申请资源的浪费)
  class User(){
    var age:Int=30
  }
  //情况二:调用能正常执行
//  class User() extends Serializable {
//    var age:Int=30
//  }

}

对象序列化后才能在网络中传输:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SMTBIIc5-1648966125070)(C:\Users\jing\AppData\Roaming\Typora\typora-user-images\image-20220403135044126.png)]

报错:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LZFlWbuQ-1648966125070)(C:\Users\jing\AppData\Roaming\Typora\typora-user-images\image-20220403133944571.png)]

闭包检查源码分析:

匿名函数用到函数之外的内容会改变生命周期。

// 程序代码:
 rdd2.foreach(num=>{
      println(user.age+num)
    })
// 由  foreach
 /**
   * Applies a function f to all elements of this RDD.
   */
  def foreach(f: T => Unit): Unit = withScope {
    val cleanF = sc.clean(f)
    sc.runJob(this, (iter: Iterator[T]) => iter.foreach(cleanF))
  }
// runJob之前,先sc.clean(f)
追踪clean:
private[spark] def clean[F <: AnyRef](f: F, checkSerializable: Boolean = true): F = {
    ClosureCleaner.clean(f, checkSerializable)
    f
  }
//ClosureCleaner 闭包 checkSerializable 默认检查是否序列化
//追踪 clean
def clean(
      closure: AnyRef,
      checkSerializable: Boolean = true,
      cleanTransitively: Boolean = true): Unit = {
    clean(closure, checkSerializable, cleanTransitively, Map.empty)
  }
//再点clean
ClosureCleaner$
 private def clean(
      func: AnyRef,
      checkSerializable: Boolean,
      cleanTransitively: Boolean,
      accessedFields: Map[Class[_], Set[String]]): Unit = {

    // most likely to be the case with 2.12, 2.13
    // so we check first
    // non LMF-closures should be less frequent from now on
    val lambdaFunc = getSerializedLambda(func)

     //判断是不是闭包,如果是闭包要验证是不是能序列化
    if (!isClosure(func.getClass) && lambdaFunc.isEmpty) {
      logDebug(s"Expected a closure; got ${func.getClass.getName}")
      return
    }
     ......
     //检查能否序列化
     if (checkSerializable) {
         //确保能够序列化
      ensureSerializable(func)
    }
 }

// isClosure
// Check whether a class represents a Scala closure
  private def isClosure(cls: Class[_]): Boolean = {
    cls.getName.contains("$anonfun$")
  }
//anonfun :cls 类名中是不是包含匿名函数anonfun 这是2.12 2.13之前的判断,之后又增加了&& lambdaFunc.isEmpty的判断闭包

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值