根据Spark文档,最好在RDD转换中使用匿名或scala对象函数。我有下一个代码的对象:
object Util {
val someManager = new Manager()
def process(data: String) = someManager.manage(data)
}
我接下来称之为:
myRDD.map(Util.process)
如何将Util对象序列化并发送给spark worker?管理员是否会每次或每次只发送一次?将经理人的实例多少次发送给工人?
答案 0 :(得分:2)
如何将Util对象序列化并发送给spark worker?是否 管理员将被创建为每次或仅一次发送?多少 经理的实例将被派往火花工人?
Util.process
在编译时包含在AbstractFunction1
内,它实现了apply
方法,这是序列化的方法。您可以在反编译此代码时看到这一点(这是从JVM字节代码重建的):
par.map(new AbstractFunction1() {
public static final long serialVersionUID = 0L;
public final void apply(String data)
{
Util..MODULE$.process(data);
}
}, ClassTag..MODULE$.Unit());
}
我们想要看到的另一件事是如何将Util
对象编译成字节代码:
public final class Util$
{
public static final MODULE$;
private final Manager someManager;
public Manager someManager()
{
return this.someManager;
}
private Util$()
{
MODULE$ = this;
this.someManager = new Manager();
}
public void process(String data)
{
someManager().manage(data);
}
}
这意味着我们仍然有Manager
的实例字段。会发生什么事情,Spark将使用它ClosureCleaner
来清除Util
的实例,因为它不需要它,但是它需要将Manager
实例序列化为工人,每次它都需要调用map
。这并不意味着会有多个Manager
实例,它只是意味着需要对单个实例进行序列化并通过线路发送。我们可以在ClosureCleaner
的文档中看到详细信息:
- 机制是遍历封闭闭包的层次结构,并且将所有未实际使用的引用中的任何引用置空 通过开始闭合,但仍包括在 编译匿名类。请注意,简单地说它是不安全的 将封闭的闭包变异,就像其他代码路径可能依赖的那样 在他们。相反,我们克隆每个封闭的闭包并设置 相应的父指针。
答案 1 :(得分:-1)
Util需要可序列化,否则你会得到一个例外。经理将每次发送,例如每项任务。您只能为每个分区发送一次,为此您需要先播放它,请参阅http://spark.apache.org/docs/latest/programming-guide.html