目录
- 1、何为闭包
- 2、原理分析
- 3、spark 相关算子
1、何为闭包
闭包跟在RDD的算子中操作作用域外部的变量有关,所谓RDD算子中,操作作用域外部的变量,指的是,类似下面的语句: val a = 0; rdd.foreach(i -> a += i)。此时,对rdd执行的foreach算子的作用域,其实仅仅是它的内部代码,但是这里却操作了作用域外部的a变量,根据不同的编程语言的语法,这种功能是可以做到的,而这种现象就叫做闭包。
闭包简单来说,就是操作的不属于一个作用域范围的变量!
2、原理分析
在作业提交到集群执行的模式下(无论是client或cluster模式,作业都是在集群中运行的),为了分布式并行执行作业,spark会将RDD算子操作,分散成多个task,放到集群中的多个节点上的executor进程中去执行,每个task执行的是相同的代码,但是却是处理不同的数据。
在提交作业的task到集群去执行之前,spark会先在driver端处理闭包,spark中的闭包,特指那些不在算子的作用域内部,但是在作用域外部却被算子处理和操作了的变量。而算子代码的执行也需要这些变量才能顺利执行,此时这些闭包变量会被序列化成多个副本,然后每个副本都发送到各个executor进程中,供那个executor进程运行的task执行代码时使用。闭包变量发送到executor进程中之后,就变成了一个一个独立的变量副本了,这就是最关键的一点。此时在executor进程中,执行task和算子代码时,访问的闭包变量,也仅仅只是当前executor进程中的一个变量副本而已了,此时虽然在driver进程中,也有一个变量副本,但是却完全跟各个executor进程中的变量副本不是一个东西。此时各个executor进程对于自己内存中的变量副本进行操作,即使改变了变量副本的值,但是对于driver端的程序,是完全感知不到的driver端的变量没有被进行任何操作。
综上所述,在你使用集群模式运行作业的时候,切忌在算子内部,对作用域外面的闭包变量进行改变其值的操作,
因为那没有任何意义,算子仅仅会在executor进程中,改变变量副本的值,对于driver端的变量没有任何影响,我们也获取不到executor端的变量副本的值。
此时,两个executor :
一个executor的局部计算结果是6,
一个executor的局部计算结果是9,
此时,如果你在driver端,去打印这个a变量的值,
此时是没有任何意义的 会打印出来一个0。
3、spark 相关算子
希望在集群模式下,对某个driver端的变量,进行分布式并行地全局性的修改,可以使用Spark提供的Accumulator,全局累加器
一个Accumulator的高级用法,自定义Accumulator,实现任意机制和算法的全局计算器。