我知道我可以将Seq[Future[T]]
转换为Future[Seq[T]]
通过
val seqFuture = Future.sequence(seqOfFutures)
seqFuture.map((seqT: Seq[T]) => {...})
我现在的问题是,我按顺序排列了700个期货,我希望能够控制其中有多少期货被并行解决,因为每个未来将调用内部休息api,同时有700个请求就像对该服务器发起dos攻击一样。
我宁愿一次只解决10个期货的问题。
我怎样才能做到这一点?
尝试pamu's answer我看到了错误:
[error] /home/philipp/src/bluebat/src/main/scala/com/dreamlines/metronome/service/JobFetcher.scala:32:44: com.dreamlines.commons.LazyFuture[A] does not take parameters
[error] val batch = Future.sequence(c.map(_()))
[error] ^
[error] /home/philipp/src/bluebat/src/main/scala/com/dreamlines/metronome/service/JobFetcher.scala:32:28: no type parameters for method sequence: (in: M[scala.concurrent.Future[A]])(implicit cbf: scala.collection.generic.CanBuildFrom[M[scala.concurrent.Future[A]],A,M[A]], implicit executor: scala.concurrent.ExecutionContext)scala.concurrent.Future[M[A]] exist so that it can be applied to arguments (List[Nothing])
[error] --- because ---
[error] argument expression's type is not compatible with formal parameter type;
[error] found : List[Nothing]
[error] required: ?M[scala.concurrent.Future[?A]]
[error] val batch = Future.sequence(c.map(_()))
[error] ^
[error] /home/philipp/src/bluebat/src/main/scala/com/dreamlines/metronome/service/JobFetcher.scala:32:42: type mismatch;
[error] found : List[Nothing]
[error] required: M[scala.concurrent.Future[A]]
[error] val batch = Future.sequence(c.map(_()))
[error] ^
[error] /home/philipp/src/bluebat/src/main/scala/com/dreamlines/metronome/service/JobFetcher.scala:32:36: Cannot construct a collection of type M[A] with elements of type A based on a collection of type M[scala.concurrent.Future[A]].
[error] val batch = Future.sequence(c.map(_()))
[error] ^
[error] four errors found
答案 0 :(得分:4)
简单foldLeft
可用于控制一次同时运行的期货数量。
首先,让我们创建一个名为LazyFuture
case class LazyFuture[+A](f: Unit => Future[A]) {
def apply() = f()
}
object LazyFuture {
def apply[A](f: => A)(implicit ec: ExecutionContext): LazyFuture[A] = LazyFuture(_ => Future(f))
def apply[A](f: => Future[A])(implicit ec: ExecutionContext): LazyFuture[A] = LazyFuture(_ => f)
}
LazyFuture
阻止未来立即投放
val list: List[LazyFuture[A]] = ...
list.grouped(concurFactor).foldLeft(Future.successful(List.empty[A])){ (r, c) =>
val batch = Future.sequence(c.map(_()))
batch.flatMap(values => r.map(rs => rs ++ values))
}
相应地更改concurFactor
以同时运行多个期货。
concurFactor
将同时运行一个未来
concurFactor
中的2个将同时运行两个期货
依旧......
def executeBatch[A](list: List[LazyFuture[A]])(concurFactor: Int) =
list.grouped(concurFactor).foldLeft(Future.successful(List.empty[A])){ (r, c) =>
val batch = Future.sequence(c.map(_()))
r.flatMap(rs => batch.map(values => rs ++ values))
}
case class LazyFuture[+A](f: Unit => Future[A]) {
def apply() = f()
}
object LazyFuture {
def apply[A](f: => A)(implicit ec: ExecutionContext): LazyFuture[A] = LazyFuture(_ => Future(f))
def apply[A](f: => Future[A])(implicit ec: ExecutionContext): LazyFuture[A] = LazyFuture(_ => f)
}
def executeBatch[A](list: List[LazyFuture[A]])(concurFactor: Int)(implicit ec: ExecutionContext): Future[List[A]] =
list.grouped(concurFactor).foldLeft(Future.successful(List.empty[A])) { (r, c) =>
val batch = Future.sequence(c.map(_ ()))
r.flatMap(rs => batch.map(values => rs ++ values))
}
您还可以通过限制执行池中的线程数来限制计算资源。但是,这种解决方案并不灵活。就个人而言,我不喜欢它。
val context: ExecutionContext =
ExecutionContext.fromExecutor(Executors.newFixedThreadPool(8))
您必须记住传递正确的执行上下文,这是一个隐式值。有时我们不知道哪个隐含在范围内。这是一辆马车
当未来构建如下
时val foo = Future {
1 + 2
} // future starts executing
LazyFuture(foo) // Not a right way
foo
已经开始执行,无法控制。
构建LazyFuture
val foo = LazyFuture {
1 + 2
}
或
val foo = LazyFuture {
Future {
1 + 2
}
}
package main
import scala.concurrent.{Await, ExecutionContext, Future}
import scala.concurrent.duration.Duration
object Main {
case class LazyFuture[A](f: Unit => Future[A]) {
def apply(): Future[A] = f()
}
object LazyFuture {
def apply[A](f: => A)(implicit ec: ExecutionContext): LazyFuture[A] = LazyFuture(_ => Future(f))
def apply[A](f: => Future[A]): LazyFuture[A] = LazyFuture(_ => f)
}
def executeBatch[A](list: List[LazyFuture[A]])(concurFactor: Int)
(implicit ec: ExecutionContext): Future[List[A]] =
list.grouped(concurFactor).foldLeft(Future.successful(List.empty[A])) { (r, c) =>
val batch = Future.sequence(c.map(_ ()))
r.flatMap(rs => r.map(values=> rs ++ values))
}
def main(args: Array[String]): Unit = {
import scala.concurrent.ExecutionContext.Implicits.global
val futures: Seq[LazyFuture[Int]] = List(1, 2, 3, 4, 5).map { value =>
LazyFuture {
println(s"value: $value started")
Thread.sleep(value * 200)
println(s"value: $value stopped")
value
}
}
val f = executeBatch(futures.toList)(2)
Await.result(f, Duration.Inf)
}
}
答案 1 :(得分:3)
并发属于Scala Future
由ExecutionContext
控制。请注意,期货在创建后立即开始在上下文中执行,因此ExecutionContext
的{{1}}并不重要。在从序列创建原始期货时,您必须提供适当的上下文。
默认上下文Future.sequence
(通常通过ExecutionContext.global
导入)使用与处理器核心一样多的线程,但它也可以为阻塞任务创建许多其他线程,这些线程包含在{{1 }}。这通常是理想的行为,但它不适合您的问题。
幸运的是,您可以使用import scala.concurrent.ExecutionContext.Implicits.global
方法来包装Java线程池。例如:
scala.concurrent.blocking
当然也可以隐含地提供上下文:
ExecutionContext.fromExecutor