繁体   English   中英

Spark-使用不可序列化的成员序列化对象

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM