当前位置: 首页 > 面试题库 >

在Spark中,在所有工作线程上拥有静态对象的正确方法是什么?

郤仰岳
2023-03-14
问题内容

我一直在查看Spark的文档,其中提到了这一点:

Spark的API在很大程度上依赖于在驱动程序中传递函数以在集群上运行。有两种推荐的方法可以做到这一点:

匿名函数语法,可用于简短的代码段。全局单例对象中的静态方法。例如,您可以定义对象MyFunctions,然后传递MyFunctions.func1,如下所示:

object MyFunctions {   def func1(s: String): String = { ... } }

myRdd.map(MyFunctions.func1) 

请注意,虽然也可以在类实例中传递对方法的引用(与单例对象相对),但这需要将包含该类的对象与方法一起发送。例如,考虑:

class MyClass {   
  def func1(s: String): String = { ... }   
  def doStuff(rdd: RDD[String]): RDD[String] = { rdd.map(func1) } 
} 

在这里,如果我们创建一个新的MyClass并在其上调用doStuff,则其中的映射将引用该MyClass实例的func1方法,因此需要将整个对象发送到集群。它类似于写作 rdd.map(x => this.func1(x))。

现在我的疑问是,如果您在单例对象上具有属性(应该等同于静态属性),将会发生什么。稍作改动的相同示例:

object MyClass {   
  val value = 1   
  def func1(s: String): String = { s + value }   
} 

myRdd.map(MyClass.func1) 

因此,该函数仍然是静态引用的,但是Spark通过尝试序列化所有引用的变量又走了多远?它会序列化value还是在远程工作进程中再次初始化?

此外,在所有情况下,我在一个单例对象中都有一些重型模型,我想找到一种正确的方法将其序列化为工作者,同时保持从任何地方从单例中引用它们的能力,而不是像这样将它们传递给其他人。相当深的函数调用堆栈中的函数参数。

任何有关Spark序列化方式/方式/时间的深入信息将不胜感激。


问题答案:

这不是关于Spark的问题,而是关于Scala如何生成代码的问题。请记住,Scalaobject几乎是一个充满静态方法的Java类。考虑一个像这样的简单示例

object foo {

  val value = 42

  def func(i: Int): Int = i + value

  def main(args: Array[String]): Unit = {
    println(Seq(1, 2, 3).map(func).sum)
  }

}

那将被翻译成3个Java类。其中之一将是闭包,该闭包是map方法的参数。javap在该类上使用将产生以下内容:

public final class foo$$anonfun$main$1 extends scala.runtime.AbstractFunction1$mcII$sp implements scala.Serializable {
  public static final long serialVersionUID;
  public final int apply(int);
  public int apply$mcII$sp(int);
  public final java.lang.Object apply(java.lang.Object);
  public foo$$anonfun$main$1();
}

请注意,没有任何字段。如果您查看反汇编的字节码,它所做的就是调用func()方法。在Spark中运行时,这是将被序列化的实例;由于它没有字段,因此不需要序列化。

至于您的问题,即如何初始化静态对象,您可以在关闭开始时调用一个幂等的初始化函数。第一个将触发初始化,随后的调用将成为无操作。不过,清理工作要复杂得多,因为我不熟悉执行“在所有执行程序上运行此代码”之类的API。

如果需要清理,一种有用的方法在此博客的“ setup()和cleanup()”部分中进行了说明。

编辑:只是为了澄清,这是实际进行调用的方法的反汇编。

public int apply$mcII$sp(int);
  Code:
   0:   getstatic       #29; //Field foo$.MODULE$:Lfoo$;
   3:   iload_1
   4:   invokevirtual   #32; //Method foo$.func:(I)I
   7:   ireturn

了解它如何仅引用保存单例的静态字段并调用该func()方法。



 类似资料:
  • 问题内容: 我需要在Spark中所有执行程序的函数中使用不可序列化的第三方类,例如: 我希望有一个静态变量,我可以为每个执行者初始化一次,并且可以一遍又一遍地使用它。 由于它不是可序列化的,因此无法在驱动器中对其进行一次初始化并进行广播。 注意:这是一个答案(在所有worker上拥有静态对象的正确方法是什么),但这仅适用于Scala。 问题答案: 您可以使用或。这是从Learning Spark

  • 护士通过请购单向三种不同供应品中的一种订购医疗供应品,所有供应品均由供应商提供。 护士 由于项目可以是三种类型中的一种,并且一个项目的申请可能还不存在,所以申请表具有3种供应类型的外键。 问题是:我正确列出的外键都指向3个不同的表,除了其中一个之外,每个条目都没有相应的外键。 我得到以下错误: 第1行错误:ORA-02091:事务回滚 ORA-02291:违反完整性约束(MMM1339.ITEMN

  • 问题内容: 我刚开始学习Java,并编写了一个使用静态字段进行测试的类。一切正常,但是在Eclipse中,我看到一个图标,将其悬停时显示为:“应该以 静态方式访问 CarCounter类型的静态方法getCounter 。” 那么正确的方法是什么? 这是课程: 这是我尝试访问变量计数器的地方: 问题答案: 静态字段和方法不属于特定的对象,而是属于一个类,因此您应该从该类而不是对象中访问它们: 并不

  • 问题内容: 让我们假设在我的编码环境中强加了一条规则(或一条经验法则),该类上的任何不使用,修改或以其他方式需要任何实例变量来完成其工作的方法都应设为静态。是否有任何固有的编译时间,运行时或其他缺点? (为进一步澄清而编辑) 我知道这个问题有些悬而未决,因此我对此表示歉意。我的询问意图主要是在“辅助”方法的背景下进行的。实用程序类(带有私有CTOR,因此无法实例化)作为我们已经做过的静态方法的持有

  • 问题内容: 我正在尝试在react-admin项目中使用多个dataproviders,但出现错误: 我有这样的: 我有文件: 这是正确的方法吗? 问题答案: 您不是在调用找到的对象,而是在从数组中调用映射对象。您可以这样解决:

  • 问题内容: 我正在进行代码审查,并遇到了一个使用所有静态方法的类。入口方法接受几个参数,然后开始调用其他静态方法,并传递入口方法接收到的所有或某些参数。 它不像具有大量不相关的实用程序功能的Math类。在我自己的常规编程中,我很少编写Resharper弹出并说“这可能是静态方法”的方法,而当我这样做时,它们往往是无用的实用方法。 这种模式有什么问题吗?如果类的状态保存在字段和属性中或使用参数在静态