Apache spark 在pyspark中将python函数传递给Scala RDD

Apache spark 在pyspark中将python函数传递给Scala RDD,apache-spark,pyspark,rdd,py4j,Apache Spark,Pyspark,Rdd,Py4j,我有一个scala库,它(简单地说)接收一个函数,将其应用于一个RDD并返回另一个RDD def runFunction(rdd: RDD, function: Any => Any) = { .... val res = rdd.map(function) ... } 在scala中,用法是 import mylibrary.runFunction runFunction(myRdd, myScalaFun) 这个库打包在一个jar中,我现在也要在pytho

我有一个scala库,它(简单地说)接收一个函数,将其应用于一个RDD并返回另一个RDD

def runFunction(rdd: RDD, function: Any => Any) = {
    ....
    val res = rdd.map(function) 
    ...
}
在scala中,用法是

import mylibrary.runFunction
runFunction(myRdd, myScalaFun)
这个库打包在一个jar中,我现在也要在python中使用它。我想做的是用Python加载这个库,并向它传递一个Python函数。Python中的用法如下:

spark._jvm.mylibrary.runFunction(myPythonRdd, myPythonFun)

这将允许我使用python函数和Scala函数,而无需将整个库移植到python。这是否可以通过在Python和JVM之间来回切换的Spark功能实现?

PySpark中Python和JVM的通信方式有一些微妙之处。桥接器使用Java对象,即
JavaRDD
和非
RDD
,这些对象需要在Scala中显式取消绑定。由于Scala函数采用
RDD
,因此需要在Scala中编写一个包装器,该包装器接收
JavaRDD
,并首先执行取消装箱:

def runFunctionWrapper(jrdd: JavaRDD, ...) = {
  runFunction(jrdd.rdd, ...)
}
那就这样说吧

spark._jvm.mylibrary.runFunctionWrapper(myPythonRdd._jrdd, ...)
请注意,根据Python约定,
\uJRDD
被视为Python
RDD
类的私有成员,因此这实际上依赖于未记录的实现细节。这同样适用于
SparkContext
\u jvm
成员

真正的问题是为了应用
函数
,将Scala回调到Python中。在PySpark中,Python RDD的
map()
方法创建
org.apache.spark.api.Python的一个实例
.PythonFunction
,它保存对Python映射器函数及其环境的pickle引用。然后,每个RDD分区被序列化,并与通过TCP发送到与Spark executor共址的Python进程的pickle内容一起,在该进程中,分区被反序列化并迭代。最后,结果再次序列化并发送回执行器。整个过程由
org.apache.spark.api.python.PythonRunner
的一个实例进行编排。这与围绕Python函数构建包装器并将其传递给
RDD
实例的
map()
方法非常不同


我认为最好是简单地在Python中复制
runFunction
的功能,或者(在性能方面更好)在Scala中复制
myPythonFun
的功能。或者,如果你所做的事情可以交互完成,那么按照@EnzoBnl的建议,利用齐柏林飞艇或Polynote等多语言笔记本环境。

在我看来,所有这些很快就会变得难以维护。。。如果您不只是因为需要/喜欢python生态系统(对于dataviz,ml…)而使用scala,我建议您考虑一下,使用fine Spark支持,将两种语言无缝地混合到一个笔记本中。