(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的判断闭包