Java 如何从spark设置和获取静态变量?

Java 如何从spark设置和获取静态变量?,java,apache-spark,spark-streaming,Java,Apache Spark,Spark Streaming,我有这样一门课: public class Test { private static String name; public static String getName() { return name; } public static void setName(String name) { Test.name = name; } public static void print() { Sys

我有这样一门课:

public class Test {
    private static String name;

    public static String getName() {
        return name;
    }

    public static void setName(String name) {
        Test.name = name;
    }

    public static void print() {
        System.out.println(name);
    }

}
在我的Spark驱动程序中,我这样设置名称并调用
print()
命令:

public final class TestDriver{

    public static void main(String[] args) throws Exception {
        SparkConf sparkConf = new SparkConf().setAppName("TestApp");
        // ...
        // ...
        Test.setName("TestName")
        Test.print();
        // ...
    }
}

但是,我得到了一个
NullPointerException
。如何将值传递给全局变量并使用它?

驱动程序进程中的类副本不是执行器中的副本。它们不在同一个
类加载器中,甚至不在同一个JVM中,甚至不在同一台机器上。在驱动程序上设置静态变量不会对其他副本产生任何影响,因此您会远程发现它为空。

好的,基本上有两种方法可以将主机已知的值传递给执行器:

  • 将该值放入要序列化给执行者以执行任务的闭包中。这是最常见的一款,非常简单/优雅。样本和文档
  • 使用数据创建广播变量。这对于大数据量的不可变数据很好,因此您希望保证只发送一次。如果反复使用相同的数据也很好。样本和文档
  • 在这两种情况下都不需要使用静态变量。但是,如果您确实希望executor VM上有可用的静态值,则需要执行以下操作之一:

  • 如果值是固定的,或者executor节点(位于jar内部等)上的配置是可用的,那么您可以拥有一个延迟val,从而保证只初始化一次
  • 您可以使用使用上述两个选项之一的代码调用mapPartitions(),然后将值存储在静态变量/对象上。mapPartitions保证每个分区只运行一次(比每行运行一次要好得多),并且对于这类事情(初始化DB连接等)非常有用
  • 希望这有帮助

    注:至于你们例外:我只是在那个代码样本上并没有看到它,我打赌它发生在其他地方


    编辑以获得额外澄清:lazy val解决方案只是Scala,不涉及火花

    object MyStaticObject
    {
      lazy val MyStaticValue = {
         // Call a database, read a file included in the Jar, do expensive initialization computation, etc
         4
      }
    } 
    

    由于每个执行器对应一个JVM,一旦类被加载,MyStaticObject
    就会被初始化。
    lazy
    关键字保证了
    MyStaticValue
    变量只有在第一次实际请求时才会被初始化,并且此后一直保持其值。

    我想再添加一种方法,只有当您有一些变量在运行时作为参数传递时,这才有意义

    spark配置-->
    --conf“spark.executor.extraJavaOptions=-DcutomField=${value}”
    和 当您需要转换中的数据时可以调用
    System.getProperty(“cutomField”)

    你可以找到更多的细节

    注:当我们有大量变量时,上述讨论没有意义
    . 在这种情况下,我更喜欢@Daniel Langdon方法。

    我想在DanielL的回答中再补充一点


    当使用static关键字声明变量时,JVM会在类加载过程中加载它,因此如果您创建jar并设置Java/scala类中静态字段的初始值存储在jar中,那么工作人员可以直接使用它。但是,如果您在驱动程序中更改静态字段的值,工作人员只能看到分配到Jar中的初始值,而您更改的值将不会反映出来,因此您需要再次复制新Jar或需要手动将类复制到所有执行器中。

    从您的代码来看,这与Spark完全无关。主程序和其他程序一样,我看不出有什么问题。它必须是在一些省略的代码上。@DanielL,我在标准java应用程序上尝试了这一点,并且成功了,因此,我假设问题在于Spark framework的任务并行化和分发行为。你能把整个堆栈跟踪吗?我想说的是,问题可能出在上面没有提到的一行……使用“全局变量”,您是指在驱动程序上设置一次并在所有工作人员中使用的某个值吗?或者随着工作的进行而设置和更新的可变共享结构?还要注意,您的示例可能不是再现问题的最小集合。最有可能的情况是,
    Test.print()
    在一个闭包中被调用,以执行某些Spark操作。@SeanOwen,我如何处理这个问题?有什么建议吗?同样的代码在Hadoop MapReduce框架上工作。@SeanOwn和@DanielL,好的,所以我可以通过使用flatMap()方法调用函数来传递和保留值。e、 g
    JavaRDD wordsE=lines.flatMap(新FlatMapFunction(){@Override public Iterable call(String s){Test.setName(“TestName”)Test.print()
    但是,RDD中的每一行都会调用这些函数。如何只调用一次?基本上,我正在寻找与setup()等效的函数这是在MapReduce框架中找到的。啊,好吧,至少现在有一个RDD!我仍然认为如果你想知道异常发生在哪里,你应该提供一个更好的代码示例,但是现在我知道你要做什么了,我将发布一个答案…DanielL-你是一个明星!感谢你对选项的解释。这正是我想要的我在找。非常感谢:-)@DanielL.你能为你的第一个解决方案给出一个示例代码吗?你能给出一个关于lazy val解决方案的示例吗?进行了编辑,添加了指向正确文档的链接,并在末尾添加了lazy val示例。干杯!我可能会晚一点,但是…使用
    lazy val
    映射有什么区别吗分区
    就调用初始化代码的次数而言?对于
    mapPartitions
    很明显,它是为每个分区调用的,但是
    lazy val
    cased又如何呢?它仍然是为每个分区调用一次,还是为每个执行器调用一次?后者。根据输入大小和配置,您可能会说100任务和5个执行者,这意味着你