Spark闭包与序列化
Spark的官方文档再三强调那些将要作用到RDD上的操作,不管它们是一个函数还是一段代码片段,它们都是“闭包”,Spark会把这个闭包分发到各个worker节点上去执行,这里涉及到了一个容易被忽视的问题:闭包的“序列化”。
显然,闭包是有状态的,这主要是指它牵涉到的那些自由变量以及自由变量依赖到的其他变量,所以,在将一个简单的函数或者一段代码片段(就是闭包)传递给类似RDD.map这样的操作前,Spark需要检索闭包内所有的涉及到的变量(包括传递依赖的变量),正确地把这些变量序列化之后才能传递到worker节点并反序列化去执行。如果在涉及到的所有的变量中有任何不支持序列化或没有指明如何序列化自己时,你就会遇到这样的错误:
org.apache.spark.SparkException: Task not serializable
在下面的例子中,我们从kafka中持续地接收json消息,并在spark-streaming中将字符串解析成对应的实体:
object App {
private val config = ConfigFactory.load("my-streaming.conf")
case class Person (firstName: String,lastName: String)
def main(args: Array[String]) {
val zkQuorum = config.getString("kafka.zkQuorum")
val myTopic = config.getString("kafka.myTopic")
val myGroup = config.getString("kafka.myGroup")
val conf = new SparkConf().setAppName("my-streaming")
val ssc = new StreamingContext(conf, Seconds())
val lines = KafkaUtils.createStream(ssc, zkQuorum, myGroup, Map(myTopic -> ))
//this val is a part of closure, and it's not serializable!
implicit val formats = DefaultFormats
def parser(json: String) = parse(json).extract[Person].firstName
lines.map(_._2).map(parser).print
....
ssc.start()
ssc.awaitTerminationOrTimeout()
ssc.stop()
}
}
这段代码在执行时就会报如下错误:
org.apache.spark.SparkException: Task not serializable
Caused by: java.io.NotSerializableException: org.json4s.DefaultFormats$
问题的症结就在于:闭包没有办法序列化。在这个例子里,闭包的范围是:函数parser以及它所依赖的一个隐式参数: formats , 而问题就出在这个隐式参数上, 它的类型是DefaultFormats,这个类没有提供序列化和反序列自身的说明,所以Spark无法序列化formats,进而无法将task推送到远端执行。
隐式参数formats是为extract准备的,它的参数列表如下:
org.json4s.ExtractableJsonAstNode#extract[A](implicit formats: Formats, mf: scala.reflect.Manifest[A]): A = ...
找到问题的根源之后就好解决了。实际上我们根本不需要序列化formats, 对我们来说,它是无状态的。所以,我们只需要把它声明为一个全局静态的变量就可以绕过序列化。所以改动的方法就是简单地把implicit val formats = DefaultFormats的声明从方法内部迁移到App Object的字段位置上即可。
object App {
private val config = ConfigFactory.load("my-streaming.conf")
case class Person (firstName: String,lastName: String)
//As Object field, global, static, no need to serialize
implicit val formats = DefaultFormats
def main(args: Array[String]) {
val zkQuorum = config.getString("kafka.zkQuorum")
val myTopic = config.getString("kafka.myTopic")
val myGroup = config.getString("kafka.myGroup")
val conf = new SparkConf().setAppName("my-streaming")
val ssc = new StreamingContext(conf, Seconds())
val lines = KafkaUtils.createStream(ssc, zkQuorum, myGroup, Map(myTopic -> ))
def parser(json: String) = parse(json).extract[Person].firstName
lines..map(_._2).map(parser).print
....
ssc.start()
ssc.awaitTerminationOrTimeout()
ssc.stop()
}
}
这里再提供另外一个很好的例子:
这个例子很好演示了解决类似问题的方案:“把类成员变量拷贝一份到闭包中” ,不然整个对象都需要被序列化!
最后我们来总结一下应该如何正确的处理Spark Task闭包的序列化问题。首先你需要对Task涉及的闭包的边界要有一个清晰的认识,要尽量地控制闭包的范围和牵涉到的自由变量,一个非常值得警惕的地方是:尽量不要在闭包中直接引用一个类的成员变量和函数,这样会导致整个类实例被序列化。这样的例子在Spark文档中也有提及,如下:
class MyClass {
def func1(s: String): String = { ... }
def doStuff(rdd: RDD[String]): RDD[String] = { rdd.map(func1) }
}
然后,一个好的组织代码的方式是:除了那些很短小的函数,尽量把复杂的操作封装到全局单一的函数体:全局静态方法或者函数对象
如果确实需要某个类的实例参与到计算过程中,则要作好相关的序列化工作。
Spark闭包与序列化的更多相关文章
- Spark闭包 | driver & executor程序代码执行
Spark中的闭包 闭包的作用可以理解为:函数可以访问函数外部定义的变量,但是函数内部对该变量进行的修改,在函数外是不可见的,即对函数外源变量不会产生影响. 其实,在学习Spark时,一个比较难理解的 ...
- Spark:将RDD[List[String,List[Person]]]中的List[Person]通过spark api保存为hdfs文件时一直出现not serializable task,没办法找到"spark自定义Kryo序列化输入输出API"
声明:本文转自<在Spark中自定义Kryo序列化输入输出API> 在Spark中内置支持两种系列化格式:(1).Java serialization:(2).Kryo seriali ...
- spark优化:spark.serializer修改序列化方式
进行节点的数据传递,或者保存数据时都会进行序列化.spark默认的是org.apache.spark.serializer.JavaSerializer.而我们要修改成org.apache.spark ...
- Spark设置Kryo序列化缓冲区大小
背景 今天在开发SparkRDD的过程中出现Buffer Overflow错误,查看具体Yarn日志后发现是因为Kryo序列化缓冲区溢出了,日志建议调大spark.kryoserializer.buf ...
- Spark性能优化(1)——序列化、内存、并行度、数据存储格式、Shuffle
序列化 背景: 在以下过程中,需要对数据进行序列化: shuffling data时需要通过网络传输数据 RDD序列化到磁盘时 性能优化点: Spark默认的序列化类型是Java序列化.Java序列化 ...
- Spark 序列化问题
在Spark应用开发中,很容易出现如下报错: org.apache.spark.SparkException: Task not serializable at org.apache.spark.ut ...
- spark新能优化之序列化
概叙: 在任何分布式系统中,序列化都是扮演着一个重要的角色的.如果使用的序列化技术,在执行序列化操作的时候很慢,或者是序列化后的数据还是很大,那么会让分布式应用程序的性能下降很多.所以,进行Spark ...
- 【Spark调优】Kryo序列化
[Java序列化与反序列化] Java序列化是指把Java对象转换为字节序列的过程:而Java反序列化是指把字节序列恢复为Java对象的过程.序列化使用场景:1.数据的持久化,通过序列化可以把数据永久 ...
- Spark 性能相关参数配置详解-压缩与序列化篇
随着Spark的逐渐成熟完善, 越来越多的可配置参数被添加到Spark中来, 本文试图通过阐述这其中部分参数的工作原理和配置思路, 和大家一起探讨一下如何根据实际场合对Spark进行配置优化. 由于篇 ...
随机推荐
- 大数据:Hadoop(HDFS 的设计思路、设计目标、架构、副本机制、副本存放策略)
一.HDFS 的设计思路 1)思路 切分数据,并进行多副本存储: 2)如果文件只以多副本进行存储,而不进行切分,会有什么问题 缺点 不管文件多大,都存储在一个节点上,在进行数据处理的时候很难进行并行处 ...
- 关于Tfrecord
写入Tfrecord print("convert data into tfrecord:train\n") out_file_train = "/home/huadon ...
- DFS 算法模板
dfs算法模板: 1.下一层是多节点的dfs遍历 def dfs(array or root, cur_layer, path, result): if cur_layer == len(array) ...
- 彻底理解 Cookie、Session、Token
发展史 1.很久很久以前,Web 基本上就是文档的浏览而已, 既然是浏览,作为服务器, 不需要记录谁在某一段时间里都浏览了什么文档,每次请求都是一个新的HTTP协议, 就是请求加响应, 尤其是我不用记 ...
- nginx部署vue项目
nginx是一个高性能的HTTP和反向代理服务器.因此常用来做静态资源服务器和后端的反向代理服务器.本文主要记录使用nginx去部署使用vue搭建的前端项目,项目基于vue官方的脚手架vue-cli构 ...
- react hooks沉思录
将UI组件抽象为状态处理机.分为普通状态和副作用状态. 一.综述 useState:处理函数只改变引用的状态本身:副作用状态:会对引用状态以外的状态和变量进行修改:useReducer:用解藕化的机制 ...
- (11)树莓派3 有线网卡静态IP设置
https://www.cnblogs.com/10e-6/p/5778355.html 树莓派设置静态IP地址 首先终端输入: ifconfig 查看树莓派默认分配的动态IP地址. 图 1-4 配置 ...
- linux学习11 Linux基础命令及命令历史
一.Linux系统上的文件类型 1.- :常规文件:在其它程序中用f表示.比如我们用ls -l命令查看的第一个内容 [root@localhost ~]# ls -l total -rw------- ...
- CLR Exception---E0434352
什么是CLR Exception---E0434352 CLR异常是.NET应用程序生成的异常类型.异常被封装在从System.exception类派生的类中.它的异常代码是0xE0434352,代码 ...
- MYSQL中group_concat( )函数中参数的排序方法
使用mysql中的group_concat( )函数连接指定字段时,可以先对该字段进行排序. PS:是因为二刷mysql的51道题的第12题遇到的:查询和" 01 "号同学学习的课 ...