我很好奇递归建立一个连续运行的Akka期货链的最好方法,如果未来doWork
调用失败,未来应该重试3次,链条应该如果重试尝试用完,则会失败。假设所有doWork
次呼叫都通过,则返回的未来futChain
应该只完成。
object Main extends App {
val futChain = recurse(2)
def recurse(param: Int, retries: Int = 3): Future[String] {
Future {
doWorkThatMayFailReturningString(param...)
} recoverWith {
case e =>
if (retries > 0) recurse(param, retries -1)
else Future.failed(e)
} flatMap {
strRes => recurse(nextParam) //how should the res from the previous fut be passed?
}
}
futChain onComplete {
case res => println(res) //should print all the strings
}
}
String
从doWork
函数返回(我需要以某种方式修改recurse
func以返回Futrue[List[String]]
recover
还是recoverWith
?flatMap
链接这些来电是否可以答案 0 :(得分:11)
您可以像这样实施可重试的Future
:
def retry[T](f: => Future[T])(n: Int)(implicit e: ExecutionContext): Future[T] = {
n match {
case i if (i > 1) => f.recoverWith{ case t: Throwable => retry(f)(n - 1)}
case _ => f
}
}
这不是针对尾递归进行优化的,但是如果你只打算重试几次,你就不会得到堆栈溢出(我想如果它在前几次失败,无论如何,它会继续失败。
然后我会分开进行链接。如果您将有限数量的函数链接在一起,每个函数都取决于之前的(并且由于某种原因您希望聚合结果),您可以使用for
理解(flatMap
的语法糖):< / p>
for {
firstResult <- retry(Future(doWork(param)))(3)
secondResult <- retry(Future(doWork(firstResult)))(3)
thirdResult <- retry(Future(doWork(secondResult)))(3)
} yield List(firstResult, secondResult, thirdResult)
对于任意长链,您可以使用Future.sequence
(Akka库中的Futures
)并行执行这些链:
def doWork(param: String): String = ...
val parameters: List[String] = List(...)
val results: Future[List[String]] = Future.sequence(parameters.map(doWork(_)))
这将解开List[Future[String]]
到Future[List[String]]
的内容。
这是按顺序执行类似操作的一种方法:
def sequential[A, B](seq: List[A])(f: A => Future[B])(implicit e: ExecutionContext): Future[List[B]] = {
seq.foldLeft(Future.successful(List[B]())) { case (left, next) =>
left.flatMap(list => f(next).map(_ :: list))
}
}
def doWork(param: String): String = ...
val results: Future[List[String]] = sequential(parameters)(param => Future(doWork(param)))
这些功能的实现非常对您的用例敏感。如果链中的任何期货失败,上述两个函数将返回失败的期货。有时候你会想要这个,有时则不然。如果您只想收集成功的期货,并在不失败整个结果的情况下丢弃失败的期货,您可以添加额外的步骤来恢复失败。
此外,recover
和recoverWith
之间的差异是它接受的PartialFunction
类型。 recover
使用默认值替换失败的期货,recoverWith
使用其他Future
替换失败的期货。对于我的retry
,recoverWith
更合适,因为我尝试自行恢复失败的Future
。