实际问题是这样的:我打开一个用户流来填充我的一些缓存,有时候,这个流得到420异常(在很短的时间内登录尝试次数太多。)
在尝试重新建立连接之前,我应该等多久?
override def onException(ex: Exception): Unit = {
Logger.info("Exception:::" + ex.getMessage + ":::" + ex.getCause)
if (ex.getMessage.startsWith("420")) {
// Can't authenticate for now, thus has to fill up cache hole in next start
// Wait some time (How long?) Thread.sleep(5000L)
// Connect via restApi and fill up the holes in the cache
// Continue listening
}
}
答案 0 :(得分:1)
我想你必须在这里使用一些退避策略,我也不会使用sleep
,我会保持我的应用程序异步。
这可能不是您问题的严格解决方案,因为它几乎是相当可观的伪代码,但它可能是一个开始。首先我从Play借用! the timeout future definition:
import scala.language.higherKinds
import scala.concurrent.duration.FiniteDuration
import java.util.concurrent.TimeUnit
import scala.concurrent.{ExecutionContext, Future, Promise => SPromise}
import play.api.libs.concurrent.Akka
import util.Try
def timeout[A](message: => A, duration: Long, unit: TimeUnit = TimeUnit.MILLISECONDS)(implicit ec: ExecutionContext): Future[A] = {
val p = SPromise[A]()
Akka.system.scheduler.scheduleOnce(FiniteDuration(duration, unit)) {
p.complete(Try(message))
}
p.future
}
这使用Akka来安排未来的执行,并结合承诺返回未来。此时,您可以在超时未来使用flatMap
链接未来执行:
val timeoutFuture: Future[String] =
timeout("timeout", duration, TimeUnit.SECONDS)
timeoutFuture.flatMap(timeoutMessage => connectToStream())
此时连接仅在超时到期后执行,但我们仍然需要实现某种重新连接机制,为此我们可以使用recover:
def twitterStream(duration: Long = 0, retry: Int = 0): Future[Any] = {
val timeoutFuture: Future[String] =
timeout("timeout", duration, TimeUnit.SECONDS)
// check how many time we tried to implement some stop trying strategy
// check how long is the duration and if too long reset.
timeoutFuture.flatMap(timeoutMessage => connectToStream())
.recover {
case connectionLost: SomeConnectionExpiredException =>
twitterStream(duration + 20, retry + 1) // try to reconnect
case ex: Exception if ex.getMessage.startsWith("420") =>
twitterStream(duration + 120, retry + 1) // try to reconect with a longer timer
case _ =>
someDefault()
}
}
def connectToStream(): Future[String] = {
// connect to twitter
// do some computation
// return some future with some result
Future("Tweets")
}
这里发生的事情是,当从未来捕获异常时,如果该异常是420或某个连接丢失异常,则执行恢复并重新调用该函数,在duration + 20
秒后重新启动连接。
有几个注释,代码未经测试(我只能编译它),此处的退避时间也是线性的(x + y
),您可能需要查看一些指数backoff strategy最后,你需要Akka来实现在超时期限中使用的时间表(Play已经有Akka可用),以及在期货支票this SO question上使用超时的其他可能性。
不确定是否所有这些都是过度杀伤,可能会有更短更简单的解决方案。