我对Spark的代码向运行它的节点的分配机制的理解只是粗略的,当我希望为每个分区实例化一个类时,我无法在Spark的mapPartitions
API中成功运行我的代码。一个争论。
下面的代码运行良好,直到我将类MyWorkerClass
演化为需要一个参数为止:
val result : DataFrame =
inputDF.as[Foo].mapPartitions(sparkIterator => {
// (1) initialize heavy class instance once per partition
val workerClassInstance = MyWorkerClass(bar)
// (2) provide an iterator using a function from that class instance
new CloseableIteratorForSparkMapPartitions[Post, Post](sparkIterator, workerClassInstance.recordProcessFunc)
}
在我有(或选择)向类MyWorkerClass
添加构造函数参数的时间点上,上面的代码运行良好。传递的参数值在工作程序中显示为null
,而不是bar
的实际值。该参数的序列化以某种方式无法按预期工作。
您将如何处理?
我将避免添加庞大的代码CloseableIteratorForSparkMapPartitions
-它仅提供Spark友好的迭代器,甚至可能不是最优雅的实现。
据我了解,构造函数参数没有正确地传递给Spark工作者,这是因为在序列化要在Spark工作者上执行的东西时,Spark如何捕获状态。但是,实例化该类确实可以无缝地使该类中包含的繁重资产–通常可用于上述代码最后一行中提供的功能;该类似乎确实对每个分区实例化。实际上,这是使用mapPartitions
而不是map
的有效的关键用例。
参数传递给它的实例化使我难以确定如何启用或解决。在我的情况下,该参数是仅在程序开始运行后才知道的值(即使在我的作业的整个执行过程中始终不变;它实际上是程序参数)。我确实需要将其传递给类的初始化。
我试图通过提供一个函数来解决问题,该函数使用其输入参数实例化MyWorkerClass
,而不是像上面那样直接实例化,但这并没有解决问题。
问题的根本症状也不例外,只是在实例化bar
时MyWorkerClass
的值将仅为null
,而不是{{ 1}},这在我包围上面包含的代码段的代码范围中是众所周知的!