我有一个案例类Thing
的实例,我有一堆运行的查询返回Thing
的集合,如下所示:
def queries: Seq[Future[Seq[Thing]]]
我需要收集所有期货中的所有Thing
(如上所述)并将它们分组为10,000个大小相同的集合,以便将它们序列化为10,000 Thing
s的文件。
def serializeThings(Seq[Thing]): Future[Unit]
我希望它能够以这样一种方式实现,即我不会在序列化之前等待所有查询运行。只要在第一次查询的期货完成后返回10,000 Thing
s,我就想开始序列化。
如果我这样做:
Future.sequence(queries)
它将收集所有查询的结果,但我的理解是,在所有查询完成且所有map
必须适合内存时,才会调用Thing
之类的操作。一次。
使用Scala集合和并发库实现批处理流管道的最佳方法是什么?
答案 0 :(得分:1)
我认为我成功了。该解决方案基于我之前的answer。它会收集Future[List[Thing]]
结果的结果,直到达到BatchSize
的阈值。然后它调用serializeThings
future,当它完成时,循环继续其余的。
object BatchFutures extends App {
case class Thing(id: Int)
def getFuture(id: Int): Future[List[Thing]] = {
Future.successful {
List.fill(3)(Thing(id))
}
}
def serializeThings(things: Seq[Thing]): Future[Unit] = Future.successful {
//Thread.sleep(2000)
println("processing: " + things)
}
val ids = (1 to 4).toList
val BatchSize = 5
val future = ids.foldLeft(Future.successful[List[Thing]](Nil)) {
case (acc, id) =>
acc flatMap { processed =>
getFuture(id) flatMap { res =>
val all = processed ++ res
val (batch, rest) = all.splitAt(5)
if (batch.length == BatchSize) { // if futures filled the batch with needed amount
serializeThings(batch) map { _ =>
rest // process the rest
}
} else {
Future.successful(all) //if we need more Things for a batch
}
}
}
}.flatMap { rest =>
serializeThings(rest)
}
Await.result(future, Duration.Inf)
}
结果打印:
处理:列表(东西(1),东西(1),东西(1),东西(2),东西(2))
处理:清单(物品(2),物品(3),物品(3),物品(3),物品(4))
处理:列表(东西(4),东西(4))
当Thing
的{{1}} s的数量不能被BatchSize
整除时,我们必须再次呼叫serializeThings
(最后flatMap
)。我希望它有所帮助! :)
答案 1 :(得分:0)
在Future.sequence
执行您想要处理的个人未来之前,请先使用Future.sequence
。
//this can be used for serializing
def doSomething(): Unit = ???
//do something with the failed future
def doSomethingElse(): Unit = ???
def doSomething(list: List[_]) = ???
val list: List[Future[_]] = List.fill(10000)(Future(doSomething()))
val newList =
list.par.map { f =>
f.map { result =>
doSomething()
}.recover { case throwable =>
doSomethingElse()
}
}
Future.sequence(newList).map ( list => doSomething(list)) //wait till all are complete
代替newList
代,您可以使用Future.traverse
Future.traverse(list)(f => f.map( x => doSomething()).recover {case th => doSomethingElse() }).map ( completeListOfValues => doSomething(completeListOfValues))