当我遍历RDD时,我需要通过调用外部(阻塞)服务来计算数据集中的值,该怎么办?您认为如何实现这一目标?
val值:Future[RDD[Double]] = Future sequence tasks
我试图创建一个Futures列表,但由于RDD id不是Traversable,Future.sequence不合适。
我只是想知道,如果有人有这样的问题,你是怎么解决的? 我想要实现的是在单个工作节点上获得并行性,因此我可以将该外部服务称为 3000 次,每秒。
可能还有另一个解决方案,更适合火花,就像在单个主机上有多个工作节点一样。
有趣的是,您如何应对这样的挑战?感谢。
答案 0 :(得分:5)
以下是我自己的问题的答案:
val buckets = sc.textFile(logFile, 100)
val tasks: RDD[Future[Object]] = buckets map { item =>
future {
// call native code
}
}
val values = tasks.mapPartitions[Object] { f: Iterator[Future[Object]] =>
val searchFuture: Future[Iterator[Object]] = Future sequence f
Await result (searchFuture, JOB_TIMEOUT)
}
这里的想法是,我们得到分区的集合,其中每个分区被发送到特定的工作者并且是最小的工作。每个工作都包含数据,可以通过调用本机代码并发送数据来处理。
'值'集合包含从本机代码返回的数据,并且该工作在整个集群中完成。
答案 1 :(得分:2)
根据你的回答,阻塞调用是将提供的输入与RDD中的每个单独的项进行比较,我强烈考虑重写java / scala中的比较,以便它可以作为你的spark过程的一部分运行。如果比较是纯粹的"功能(没有副作用,仅取决于它的输入),它应该很容易重新实现,并且由于不必进行远程调用而降低复杂性并增加火花过程的稳定性可能会使它值得。
您的远程服务似乎不太可能每秒处理3000个呼叫,因此最好使用本地进程内版本。
如果出于某种原因绝对不可能,那么您可以创建一个RDD转换,将您的数据转换为期货的RDD,伪代码:
val callRemote(data:Data):Future[Double] = ...
val inputData:RDD[Data] = ...
val transformed:RDD[Future[Double]] = inputData.map(callRemote)
然后从那里开始,计算你的Future [Double]对象。
如果您知道远程进程可以处理多少并行性,最好放弃Future模式并接受它是瓶颈资源。
val remoteParallelism:Int = 100 // some constant
val callRemoteBlocking(data:Data):Double = ...
val inputData:RDD[Data] = ...
val transformed:RDD[Double] = inputData.
coalesce(remoteParallelism).
map(callRemoteBlocking)
你的工作可能需要一段时间,但它不应该淹没你的远程服务而且会死得很厉害。
最后一个选项是,如果输入是合理可预测的,并且结果的范围是一致的并且限于某些合理数量的输出(数百万左右),您可以使用远程服务将它们全部预先计算为数据集并查找他们在火花工作时使用联接。