播放 WS API:限制请求率

Play WS API: throttling request rates

我正在使用异步 Play WS Scala API 来查询 RESTful 服务。我想知道如何处理一个 List 包含通过 WSClient 调用的请求 URL,但每秒不超过一个请求(该服务允许每个 "only" 1 个请求每个客户第二个)。从逻辑的角度来看,这个想法是从列表中获取一个元素 (URL),发出请求,然后等待一定的时间,然后再继续处理列表中的下一个元素。

如何在不对 Play 的异步和 "as-less-threads-as-possible" 性质产生负面影响的情况下限制请求速率?

Akka 在这里有一个方便的调度程序功能:http://doc.akka.io/docs/akka/current/scala/scheduler.html

由于 Akka 已在 Play 中,因此您无需导入任何其他内容。
它不会是最干净或最容易测试的,但你可以这样:

val webserviceCall : Runnable = new Runnable {

    override def run(): Unit = {
        // do webservice call work
        // figure out if you need to make more webservice calls, and if you do:
        actorSystem.scheduler.scheduleOnce(0 seconds, 1 seconds, webserviceCall)
    }

}

actorSystem.scheduler.scheduleOnce(0 seconds, webserviceCall)

或者,您可以使用某人之前制作的这个 Akka 消息调节器:http://doc.akka.io/docs/akka/snapshot/contrib/throttle.html

我以前用过(我想是去年的 Akka 2.3)但不确定它是否仍然有效。

假设您有一个要抓取的 URL 列表:

val urls = List(
  "http://www.google.com",
  "http://whosebug.com",
  "http://www.bing.com"
)

在 Play 2.5.x 中,我们可以按顺序处理这些,并使用 akka.pattern.after 在每个调用之间强制异步延迟。我们 flatMap Web 服务调用的 Future 结果将 return 一秒钟后 相同的 值。

Future.traverse(urls) { url =>
  wsClient.url(url).get().flatMap { result =>
    // potentially process `result` here
    akka.pattern.after(1.second, actorSystem.scheduler)(Future.successful(result))
  }
} // returns Future[List[WSResponse]]

这要求您有可用的 WSClientActorSystem 组件,以及范围内的隐式 ExecutionContext


在 Play 2.4.x 和更早版本中,您可以使用 Promise.timeout:

执行相同的操作
Future.traverse(urls) { url =>
  wsClient.url(url).get().flatMap { result =>
    // potentially process `result` here
    Promise.timeout(result, 1.second)
    akka.pattern.after(1.second, actorSystem.scheduler)(Future.successful(result))
  }
}