Spark闭包 | driver & executor程序代码执行
Spark中的闭包
闭包的作用可以理解为:函数可以访问函数外部定义的变量,但是函数内部对该变量进行的修改,在函数外是不可见的,即对函数外源变量不会产生影响。
其实,在学习Spark时,一个比较难理解的点就是,在集群模式下,定义的变量和方法作用域的范围和生命周期。这在你操作RDD时,比如调用一些函数map、foreach时,访问其外部变量进行操作时,很容易产生疑惑。为什么我本地程序运行良好且结果正确,放到集群上却得不到想要的结果呢?
首先通过下边对RDD中的元素进行求和的示例,来看相同的代码本地模式和集群模式运行结果的区别:
Spark为了执行任务,会将RDD的操作分解为多个task,并且这些task是由executor执行的。在执行之前,Spark会计算task的闭包即定义的一些变量和方法,比如例子中的counter变量和foreach方法,并且闭包必须对executor而言是可见的,这些闭包会被序列化发送到每个executor。
在集群模式下,driver和executor运行在不同的JVM进程中,发送给每个executor的闭包中的变量是driver端变量的副本。因此,当foreach函数内引用counter时,其实处理的只是driver端变量的副本,与driver端本身的counter无关。driver节点的内存中仍有一个计数器,但该变量对executor是不可见的!executor只能看到序列化闭包的副本。因此,上述例子输出的counter最终值仍然为零,因为counter上的所有操作都只是引用了序列化闭包内的值。
在本地模式下,往往driver和executor运行在同一JVM进程中。那么这些闭包将会被共享,executor操作的counter和driver持有的counter是同一个,那么counter在处理后最终值为6。
但是在生产中,我们的任务都是在集群模式下运行,如何能满足这种业务场景呢?
这就必须引出一个后续要重点讲解的概念:Accumulator即累加器。Spark中的累加器专门用于提供一种机制,用于在集群中的各个worker节点之间执行时安全地更新变量。
一般来说,closures - constructs比如循环或本地定义的方法,就不应该被用来改变一些全局状态,Spark并没有定义或保证对从闭包外引用的对象进行更新的行为。如果你这样操作只会导致一些代码在本地模式下能够达到预期的效果,但是在分布式环境下却事与愿违。如果需要某些全局聚合,请改用累加器。对于其他的业务场景,我们适时考虑引入外部存储系统、广播变量等。
闭包函数从产生到在executor执行经历了什么?
首先,对RDD相关的操作需要传入闭包函数,如果这个函数需要访问外部定义的变量,就需要满足一定条件(比如必须可被序列化),否则会抛出运行时异常。闭包函数在最终传入到executor执行,需要经历以下步骤:
1. driver通过反射,运行时找到闭包访问的变量,并封装成一个对象,然后序列化该对象
2. 将序列化后的对象通过网络传输到worker节点
3. worker节点反序列化闭包对象
4. worker节点的executor执行闭包函数
简而言之,就是要通过网络传递函数、然后执行,期间会经历序列化和反序列化,所以要求被传递的变量必须可以被序列化和反序列化,否则会抛类似Error:Task not serializable: java.io.NotSerializableException when calling function outside closure only on classes not objects这样的异常。即使是本地执行时,也会按照上述的步骤执行,这也是为什么不允许在RDD内部直接操作RDD的原因(SparkContext不支持序列化)。同时,在这些算子闭包内修改外部定义的变量不会被反馈到driver端。
driver & executor
driver是运行用户编写Application 的main()函数的地方,具体负责DAG的构建、任务的划分、task的生成与调度等。job,stage,task生成都离不开rdd自身,rdd的相关的操作不能缺少driver端的sparksession/sparkcontext。
executor是真正执行task地方,而task执行离不开具体的数据,这些task运行的结果可以是shuffle中间结果,也可以持久化到外部存储系统。一般都是将结果、状态等汇集到driver。但是,目前executor之间不能互相通信,只能借助第三方来实现数据的共享或者通信。
编写的Spark程序代码,运行在driver端还是executor端呢?
先看个简单例子:通常我们在本地测试程序的时候,要打印RDD中的数据。
在本地模式下,直接使用rdd.foreach(println)或rdd.map(println)在单台机器上,能够按照预期打印并输出所有RDD的元素。
但是,在集群模式下,由executor执行输出写入的是executor的stdout,而不是driver上的stdout,所以driver的stdout不会显示这些!
要想在driver端打印所有元素,可以使用collect()方法先将RDD数据带到driver节点,然后在调用foreach(println)(但需要注意一点,由于会把RDD中所有元素都加载到driver端,可能引起driver端内存不足导致OOM。如果你只是想获取RDD中的部分元素,可以考虑使用take或者top方法)
总之,在这里RDD中的元素即为具体的数据,对这些数据的操作都是由负责task执行的executor处理的,所以想在driver端输出这些数据就必须先将数据加载到driver端进行处理。
最后做个总结:所有对RDD具体数据的操作都是在executor上执行的,所有对rdd自身的操作都是在driver上执行的。比如foreach、foreachPartition都是针对rdd内部数据进行处理的,所以我们传递给这些算子的函数都是执行于executor端的。但是像foreachRDD、transform则是对RDD本身进行一列操作,所以它的参数函数是执行在driver端的,那么它内部是可以使用外部变量,比如在SparkStreaming程序中操作offset、动态更新广播变量等。
关注微信公众号:大数据学习与分享,获取更对技术干货
Spark闭包 | driver & executor程序代码执行的更多相关文章
- Apple macOS Mojave Intel Graphics Driver组件任意代码执行漏洞
受影响系统:Apple macOS Mojave 10.14.5描述:CVE(CAN) ID: CVE-2019-8629 Apple macOS Mojave是苹果公司Mac电脑系列产品的操作系统. ...
- android 程序代码执行adb
Runtime.getRuntime().exec("adb pull /dev/graphics/fb0 C:/fb1"); Runtime. getRuntime().exec ...
- [js]js代码执行顺序/全局&私有变量/作用域链/闭包
js代码执行顺序/全局&私有变量/作用域链 <script> /* 浏览器提供全局作用域(js执行环境)(栈内存) --> 1,预解释(仅带var的可以): 声明+定义 1. ...
- day02编程语言,Python语言介绍,Python解释器安装,环境变量,Python代码执行,pip,应用程序使用文件的三步骤,变量,变量的三大组成,比较,pycharm
复习 重点: 1.进制转换:二进制 与十六进制 2.内存分布:栈区 与堆区 # 二进制1111转换十六进制 => 8 4 2 1 => f 10101100111011 => 2a7 ...
- 使用Jacoco获取 Java 程序的代码执行覆盖率
Jacoco是Java Code Coverage的缩写,顾名思义,它是获取Java代码执行覆盖率的一个工具,通常用它来获取单元测试覆盖率.它通过分析Java字节码来得到代码执行覆盖率,因此它还可以分 ...
- 编程语言分类,Python代码执行,应用程序使用文件的三步骤,变量,常量,垃圾回收机制
编程语言分为 机器语言(直接用二进制01跟计算机直接沟通交流,直接操作硬件) 优点:计算机能够直接读懂,速度快 缺点:开发效率极低 汇编语言(用简单的英文标签来表示二进制数,直接操作硬件) 优点:开发 ...
- Delphi 如何在程序中执行动态生成的Delphi代码
如何在程序中执行动态生成的Delphi代码 经常发现有人提这类问题,或者提问内容最后归结成这种问题 前些阵子有位高手写了一个“执行动态生成的代码”,这是真正的高手,我没那种功力,我只会投机取巧. 这里 ...
- 如何在程序中执行动态生成的Delphi代码
如何在程序中执行动态生成的Delphi代码 经常发现有人提这类问题,或者提问内容最后归结成这种问题 前些阵子有位高手写了一个“执行动态生成的代码”,这是真正的高手,我没那种功力,我只会投机取巧. 这里 ...
- 【Spark深入学习 -14】Spark应用经验与程序调优
----本节内容------- 1.遗留问题解答 2.Spark调优初体验 2.1 利用WebUI分析程序瓶颈 2.2 设置合适的资源 2.3 调整任务的并发度 2.4 修改存储格式 3.Spark调 ...
随机推荐
- Linux用户和组的配置文件
用户和组的主要配置文件 前两个是放用户账号相关的,后两个是放和组相关的 /etc/passwd:用户及其属性信息(名称.UID.主组ID等) #早期密码也放这里,后来发现不安全,谁都能看 /etc/s ...
- centos之间如何实现免密ssh登陆
在公司产品中,管理平台和下面的主机很多时候都要求免密,免密的逻辑到底是怎么样的呢?今天就简单看看! 首先创建两台虚机,正常情况下ssh登陆对方是需要密码的 先通过ssh-keygen生成一对秘钥 [r ...
- vue 用别名取代路径引用
在项目开发过程中有可能很多包是没有放在npm上的,许多包需要下载到本地引用,这样一来我们只能通过require的方式来引用文件,但是路径的名字就会很长 例如 import Select from '. ...
- 几道php反序列化题目
[极客大挑战 2019]PHP 提示源码泄漏,来用扫描器扫一下 扫出来www.zip,然后下载下来 有五个文件,代码审计一下 这个地方有一个可以反序列化的点,找到类 逻辑很简单,username=ad ...
- Java 中的 Lambda 表达式
Lambda表达式 Lambda 表达式是 JDK1.8 的一个新特性,又称特殊的匿名内部类,可以取代大部分的匿名内部类,语法更简洁,可以写出更优雅的 Java 代码,可以极大地优化代码结构. Lam ...
- JavaScript动态显示时间
<body> <div></div> <script> var div = document.querySelector('div'); retNowT ...
- C语言知识点复习梳理
C语言知识点复习梳理 C语言的知识点讲完了,接下来就是做一下整理与总结,然后就会进入其他知识的学习. 本文目录如下: 基础知识. 顺序程序设计. 数据类型. 标准输入输出. 进制转换. 选择结构. 循 ...
- confluence 4.2 升级至 6.10.x 记录
confluence 4.2 升级至 6.10.x 记录 首先将线上环境中的 confluence 安装目录.数据目录以及数据库进行备份,相关信息如下: 安装目录:/opt/atlassian/con ...
- 4G DTU无线数据透明传输终端
4G DTU是基于4G网络的远程无线数据透明传输终端,是一种物联网无线数据传输设备,使用公用运营商的4G网络为用户提供无线远距离数据传输功能,使用工业级32位的高性能通信处理器和工业级无线模块,以嵌入 ...
- MYSQL中inner join、left join 和 right join的区别
首先join连接是用来进行多表关联查询的,join连接方式有三种连接方式:inner join.left join 和 right join 1.inner join 可以简写成join,叫内连接,查 ...