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