处理twitter4j用户流420例外

时间:2014-08-14 15:00:33

标签: scala twitter streaming twitter4j

实际问题是这样的:我打开一个用户流来填充我的一些缓存,有时候,这个流得到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
      }
    }

1 个答案:

答案 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上使用超时的其他可能性。

不确定是否所有这些都是过度杀伤,可能会有更短更简单的解决方案。