使用Monix Observable的.onErrorRestartIf时限制重试次数?

时间:2017-07-10 14:23:01

标签: scala monix

Monix observables有apis .onErrorRestartIf(f: Throwable => Boolean).onErrorRestart(times: Int)。如何指定它应该重试的最大次数.onErrorRestartIf?

2 个答案:

答案 0 :(得分:3)

您可以根据onErrorHandleWith

构建自己的循环
def retryLimited[A](fa: Observable[A], maxRetries: Int)
  (p: Throwable => Boolean): Observable[A] = {

  // If we have no retries left, return the source untouched
  if (maxRetries <= 0) fa else
    fa.onErrorHandleWith { err =>
      // If predicate holds, do recursive call
      if (p(err)) 
        retryLimited(fa, maxRetries - 1)(p)
      else 
        Observable.raiseError(err)
    }
}

如果您不喜欢简单的功能(我这样做),您可以随时公开一些扩展方法:

implicit class ObservableExtensions[A](val self: Observable[A]) 
  extends AnyVal {

  def onErrorRetryLimited(maxRetries: Int)
    (p: Throwable => Boolean): Observable[A] = 
    retryLimited(self, maxRetries)(p)
}

请注意@JVS的答案在精神上是可以的,但可能会有问题,因为它保持共享的可变状态,这对于冷可观察者来说是不可行的。请注意如果您执行以下操作会发生什么:

val source = Observable.suspend { 
  if (Random.nextInt() % 10 != 0)
    Observable.raiseError(new RuntimeException("dummy"))
  else
    Observable(1, 2, 3)
} 

val listT = source
  .onErrorRestartIf(limitedRetries(AtomicInt(maxRetries), shouldRestart))
  .toListL

listT.runAsync // OK
listT.runAsync // Ooops, shared state, we might not have retries left

警惕Observable运营商中的可变共享状态。你当然可以这样工作,但你必须意识到其中的危险: - )

答案 1 :(得分:0)

警告:这使用共享的可变状态,对于冷可观察对象可能不正确。见亚历山德鲁的回答。

定义一个函数来执行它:

def limitedRetries(maxRetries: AtomicInt, shouldRetryOnException: Throwable => Boolean): Throwable => Boolean = 
  ex => maxRetries.decrementAndGet() > 0 && shouldRetryOnException(ex)

并在onErrorRestartIf

中使用此功能
.onErrorRestartIf(limitedRetries(AtomicInt(maxRetries), shouldRestart))

仅供参考,在这里使用monix AtomicInt ......

import monix.execution.atomic.AtomicInt