gpt4 book ai didi

java - 在 Spark JavaRDD 转换中使用可序列化的 lambda

转载 作者:搜寻专家 更新时间:2023-10-31 20:27:08 26 4
gpt4 key购买 nike

我正在尝试理解以下代码。

//文件:LambdaTest.java

package test;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;

import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

public class LambdaTest implements Ops {

public static void main(String[] args) {
new LambdaTest().job();
}

public void job() {
SparkConf conf = new SparkConf()
.setAppName(LambdaTest.class.getName())
.setMaster("local[*]");
JavaSparkContext jsc = new JavaSparkContext(conf);

List<Integer> lst = Arrays.asList(1, 2, 3, 4, 5, 6);
JavaRDD<Integer> rdd = jsc.parallelize(lst);
Function<Integer, Integer> func1 = (Function<Integer, Integer> & Serializable) x -> x * x;
Function<Integer, Integer> func2 = x -> x * x;

System.out.println(func1.getClass()); //test.LambdaTest$$Lambda$8/390374517
System.out.println(func2.getClass()); //test.LambdaTest$$Lambda$9/208350681

this.doSomething(rdd, func1); // works
this.doSomething(rdd, func2); // org.apache.spark.SparkException: Task not serializable
}
}

//文件:Ops.java

package test;

import org.apache.spark.api.java.JavaRDD;
import java.util.function.Function;

public interface Ops {

default void doSomething(JavaRDD<Integer> rdd, Function<Integer, Integer> func) {
rdd.map(x -> x + func.apply(x))
.collect()
.forEach(System.out::println);
}

}

区别在于 func1 是用 Serializable 绑定(bind)的,而 func2 不是。

查看这两个函数的运行时类,都是LambdaTest类下的匿名类

它们都用于接口(interface)中的 RDD 转换,那么这两个函数和 LambdaTest 应该是可序列化的。

如您所见,LambdaTest 没有实现 Serializable 接口(interface)。所以我认为这两个功能应该不起作用。但令人惊讶的是,func1 有效。

func2 的堆栈跟踪如下:

Serialization stack:
- object not serializable (class: test.LambdaTest$$Lambda$9/208350681, value: test.LambdaTest$$Lambda$9/208350681@61d84e08)
- element of array (index: 0)
- array (class [Ljava.lang.Object;, size 1)
- field (class: java.lang.invoke.SerializedLambda, name: capturedArgs, type: class [Ljava.lang.Object;)
- object (class java.lang.invoke.SerializedLambda, SerializedLambda[capturingClass=interface fr.leboncoin.etl.jobs.test.Ops, functionalInterfaceMethod=org/apache/spark/api/java/function/Function.call:(Ljava/lang/Object;)Ljava/lang/Object;, implementation=invokeStatic fr/leboncoin/etl/jobs/test/Ops.lambda$doSomething$1024e30a$1:(Ljava/util/function/Function;Ljava/lang/Integer;)Ljava/lang/Integer;, instantiatedMethodType=(Ljava/lang/Integer;)Ljava/lang/Integer;, numCaptured=1])
- writeReplace data (class: java.lang.invoke.SerializedLambda)
- object (class fr.leboncoin.etl.jobs.test.Ops$$Lambda$10/1470295349, fr.leboncoin.etl.jobs.test.Ops$$Lambda$10/1470295349@4e1459ea)
- field (class: org.apache.spark.api.java.JavaPairRDD$$anonfun$toScalaFunction$1, name: fun$1, type: interface org.apache.spark.api.java.function.Function)
- object (class org.apache.spark.api.java.JavaPairRDD$$anonfun$toScalaFunction$1, <function1>)
at org.apache.spark.serializer.SerializationDebugger$.improveException(SerializationDebugger.scala:40)
at org.apache.spark.serializer.JavaSerializationStream.writeObject(JavaSerializer.scala:47)
at org.apache.spark.serializer.JavaSerializerInstance.serialize(JavaSerializer.scala:81)
at org.apache.spark.util.ClosureCleaner$.ensureSerializable(ClosureCleaner.scala:312)
... 19 more

似乎如果一个函数绑定(bind)了Serializable,包含它的对象就不需要被序列化,这让我很困惑。

非常感谢对此的任何解释。

----------------------------更新------------ ------------------

我尝试过使用抽象类而不是接口(interface):

//文件:AbstractTest.java

public class AbstractTest {

public static void main(String[] args) {
new AbstractTest().job();
}

public void job() {
SparkConf conf = new SparkConf()
.setAppName(AbstractTest.class.getName())
.setMaster("local[*]");
JavaSparkContext jsc = new JavaSparkContext(conf);

List<Integer> lst = Arrays.asList(1, 2, 3, 4, 5, 6);
JavaRDD<Integer> rdd = jsc.parallelize(lst);

Ops ops = new Ops() {

@Override
public Integer apply(Integer x) {
return x + 1;
}
};

System.out.println(ops.getClass()); // class fr.leboncoin.etl.jobs.test.AbstractTest$1
ops.doSomething(rdd);
}
}

//文件:Ops.java

public abstract class Ops implements Serializable{

public abstract Integer apply(Integer x);

public void doSomething(JavaRDD<Integer> rdd) {
rdd.map(x -> x + apply(x))
.collect()
.forEach(System.out::println);
}
}

它也不起作用,即使 Ops 类与 AbstractTest 类一起编译在单独的文件中。 ops 对象的类名是 class fr.leboncoin.etl.jobs.test.AbstractTest$1。根据以下堆栈轨迹,似乎需要序列化AbstractTest才能序列化AbstractTest$1

Serialization stack:
- object not serializable (class: test.AbstractTest, value: test.AbstractTest@21ac5eb4)
- field (class: test.AbstractTest$1, name: this$0, type: class test.AbstractTest)
- object (class test.AbstractTest$1, test.AbstractTest$1@36fc05ff)
- element of array (index: 0)
- array (class [Ljava.lang.Object;, size 1)
- field (class: java.lang.invoke.SerializedLambda, name: capturedArgs, type: class [Ljava.lang.Object;)
- object (class java.lang.invoke.SerializedLambda, SerializedLambda[capturingClass=class fr.leboncoin.etl.jobs.test.Ops, functionalInterfaceMethod=org/apache/spark/api/java/function/Function.call:(Ljava/lang/Object;)Ljava/lang/Object;, implementation=invokeSpecial fr/leboncoin/etl/jobs/test/Ops.lambda$doSomething$6d6228b6$1:(Ljava/lang/Integer;)Ljava/lang/Integer;, instantiatedMethodType=(Ljava/lang/Integer;)Ljava/lang/Integer;, numCaptured=1])
- writeReplace data (class: java.lang.invoke.SerializedLambda)
- object (class fr.leboncoin.etl.jobs.test.Ops$$Lambda$8/208350681, fr.leboncoin.etl.jobs.test.Ops$$Lambda$8/208350681@4acb2510)
- field (class: org.apache.spark.api.java.JavaPairRDD$$anonfun$toScalaFunction$1, name: fun$1, type: interface org.apache.spark.api.java.function.Function)
- object (class org.apache.spark.api.java.JavaPairRDD$$anonfun$toScalaFunction$1, <function1>)
at org.apache.spark.serializer.SerializationDebugger$.improveException(SerializationDebugger.scala:40)
at org.apache.spark.serializer.JavaSerializationStream.writeObject(JavaSerializer.scala:47)
at org.apache.spark.serializer.JavaSerializerInstance.serialize(JavaSerializer.scala:81)
at org.apache.spark.util.ClosureCleaner$.ensureSerializable(ClosureCleaner.scala:312)
... 19 more

最佳答案

LambdaTest不需要是 Serializable因为它不是通过网络发送的——没有理由这样做。

另一方面func1func1必须是 Serializable因为 Spark 将使用它们来执行计算(在 RDD 上,因此必须通过线路将此代码发送到工作节点。请注意,即使您将它们全部写在同一个类中,编译后你的 lambda 将被放在单独的文件中,这要归功于整个类不必通过网络发送 -> 外部类不需要是 Serializable

至于为什么fun1有效,当您不使用类型转换时,Java 编译器将为您推断 lambda 表达式的类型。所以在这种情况下,为 fun2 生成的代码将简单地实现一个 Function (因为那是目标变量的类型)。另一方面,如果无法从上下文中推断出类型(就像您的情况一样,编译器无法知道 fun1 必须是 Serializable 因为它是 Spark 所需的功能),您可以使用类型转换作为在您的示例中明确提供一个类型。在这种情况下,编译器生成的代码将同时实现 Function。和 Serializable接口(interface),编译器不会尝试自行推断类型。

您可以在 the state of lambda 中找到它的描述在 5 下。目标输入的上下文

关于java - 在 Spark JavaRDD 转换中使用可序列化的 lambda,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31807546/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com