斯卡拉 - 来自期货的批量流

时间:2016-08-30 11:51:32

标签: scala collections future

我有一个案例类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集合和并发库实现批处理流管道的最佳方法是什么?

2 个答案:

答案 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))