我正在尝试从包含scala函数定义的字符串中定义spark(2.0)中的udf。这是片段:
val universe: scala.reflect.runtime.universe.type = scala.reflect.runtime.universe
import universe._
import scala.reflect.runtime.currentMirror
import scala.tools.reflect.ToolBox
val toolbox = currentMirror.mkToolBox()
val f = udf(toolbox.eval(toolbox.parse("(s:String) => 5")).asInstanceOf[String => Int])
sc.parallelize(Seq("1","5")).toDF.select(f(col("value"))).show
这给了我一个错误:
Caused by: java.lang.ClassCastException: cannot assign instance of scala.collection.immutable.List$SerializationProxy to field org.apache.spark.rdd.RDD.org$apache$spark$rdd$RDD$$dependencies_ of type scala.collection.Seq in instance of org.apache.spark.rdd.MapPartitionsRDD
at java.io.ObjectStreamClass$FieldReflector.setObjFieldValues(ObjectStreamClass.java:2133)
at java.io.ObjectStreamClass.setObjFieldValues(ObjectStreamClass.java:1305)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2024)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1942)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1808)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2018)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1942)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1808)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373)
at org.apache.spark.serializer.JavaDeserializationStream.readObject(JavaSerializer.scala:75)
at org.apache.spark.serializer.JavaSerializerInstance.deserialize(JavaSerializer.scala:114)
at org.apache.spark.scheduler.ResultTask.runTask(ResultTask.scala:66)
at org.apache.spark.scheduler.Task.run(Task.scala:85)
at org.apache.spark.executor.Executor$TaskRunner.run(Executor.scala:274)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
但是当我将udf定义为:
时val f = udf((s:String) => 5)
它运作得很好。这里的问题是什么?最终目标是获取一个具有scala函数defn的字符串并将其用作udf。
答案 0 :(得分:5)
正如Giovanny所说,问题在于类加载器是不同的(你可以通过在任何对象上调用.getClass.getClassLoader
来更多地研究它)。然后,当工人们试图反射你反射的功能时,所有的地狱都会崩溃。
这是一个不涉及任何类加载器hackery的解决方案。我们的想法是将反思步骤转移给工人。我们最终不得不重做反射步骤,但每个工人只需一次。我认为这是非常理想的 - 即使你只在主节点上进行一次反射,你也必须为每个工作人员做一些工作才能让他们识别这个功能。
val f = udf {
new Function1[String,Int] with Serializable {
import scala.reflect.runtime.universe._
import scala.reflect.runtime.currentMirror
import scala.tools.reflect.ToolBox
lazy val toolbox = currentMirror.mkToolBox()
lazy val func = {
println("reflected function") // triggered at every worker
toolbox.eval(toolbox.parse("(s:String) => 5")).asInstanceOf[String => Int]
}
def apply(s: String): Int = func(s)
}
}
然后,调用sc.parallelize(Seq("1","5")).toDF.select(f(col("value"))).show
就可以了。
随意评论println
- 它只是计算反射发生次数的简单方法。在spark-shell --master 'local'
只有一次,但在spark-shell --master 'local[2]'
只有一次。
UDF会立即得到评估,但在到达工作节点之前永远不会被使用,因此只能在工作者上评估惰性值toolbox
和func
。此外,由于它们很懒惰,所以每个工人只能评估一次。
答案 1 :(得分:3)
我有同样的错误,它没有显示ClassNotFoundException,因为JavaDeserializationStream类正在捕获异常,具体取决于您的环境失败,因为它无法找到您尝试从RDD执行的类/ DataSet但它不显示ClassNotFoundError。要解决这个问题,我必须生成一个包含项目中所有类的jar(包括函数和依赖项),并在jar环境中包含jar
这适用于独立群集
conf.set("spark.yarn.jars", "/fullpath/yourgeneratedjar.jar,/fullpath/otherdependencies.jar")
这对于纱线群
scipy.ndimage.filters.convolve()