[英]Spark - Serializing an object with a non-serializable member
我将在Spark上下文中提出这个问题,因为这就是我要面对的问题,但这可能是一个普通的Java问题。
在我们的Spark工作中,我们有一个Resolver
,需要在所有工作人员中使用它(在udf中使用)。 问题在于它不可序列化,我们无法将其更改为可序列化。 解决方案是将其作为可序列化的另一个类的成员。
因此,我们最终得到了:
public class Analyzer implements Serializable {
transient Resolver resolver;
public Analyzer() {
System.out.println("Initializing a Resolver...");
resolver = new Resolver();
}
public int resolve(String key) {
return resolver.find(key);
}
}
然后,我们使用Spark API broadcast
此类:
val analyzer = sparkContext.broadcast(new Analyzer())
(有关Spark广播的更多信息,请点击此处 )
然后,作为火花代码的一部分,我们继续在UDF中使用analyzer
,如下所示:
val resolve = udf((key: String) => analyzer.value.resolve(key))
val result = myDataFrame.select("key", resolve("key")).count()
所有这些都按预期工作,但是让我们纳闷。
Resolver
没有实现Serializable
,因此被标记为transient
-意味着它不会与所有者对象Analyzer
一起被序列化。
但是从上面的代码中可以清楚地看到, resolve()
方法使用resolver
,因此它不能为null。 确实不是。 该代码有效。
因此,如果该字段未通过序列化传递,则resolver
成员如何实例化?
我最初的想法是,可能在接收方(即Spark工作者)调用了Analyzer
构造函数,但随后我希望看到"Initializing a Resolver..."
行已多次打印。 但是它只打印了一次,这可能表明它在传递到广播API之前就只被调用过一次。 那么为什么resolver
不为null?
我是否缺少有关JVM序列化或Spark序列化的内容?
此代码甚至如何工作?
Spark在cluster
模式下在YARN上运行。 spark.serializer
设置为org.apache.spark.serializer.KryoSerializer
。
因此,如果该字段未通过序列化传递,则解析器成员如何实例化?
在调用kryo.readObject
时,可通过构造函数调用( new Resolver
)对其进行实例化:
kryo.readClassAndObject(input).asInstanceOf[T]
我最初的想法是,可能在接收方(即Spark工作者)调用了Analyzer构造函数,但随后我希望看到“ Initializing a Resolver ...”行已多次打印。 但是它只打印了一次,这可能表明它只被打印了一次
那不是广播变量的工作方式。 发生的情况是,当每个执行程序都需要作用域中的broadcast变量时,它首先检查其BlockManager
中是否有内存中的对象,否则,它将询问驱动程序或邻居执行程序(如果存在多个执行程序)缓存实例的实例),然后将其序列化并将其发送给他,然后他接收该实例并将其缓存在自己的BlockManager
。
TorrentBroadcast
的行为(这是默认的广播实现)中对此进行了记录:
* The driver divides the serialized object into small chunks and
* stores those chunks in the BlockManager of the driver.
*
* On each executor, the executor first attempts to fetch the object from its BlockManager. If
* it does not exist, it then uses remote fetches to fetch the small chunks from the driver and/or
* other executors if available. Once it gets the chunks, it puts the chunks in its own
* BlockManager, ready for other executors to fetch from.
*
* This prevents the driver from being the bottleneck in sending out multiple copies of the
* broadcast data (one per executor).
如果我们删除瞬态,它将失败,并且堆栈跟踪会导致Kryo
这是因为您的Resolver
类中可能存在一个字段,即使Serialable属性,即使Kryo也无法对其进行序列Serializable
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.