2016-05-16 159 views
2

我正在使用异步播放WS Scala API来查询REST式服务。我想知道如何处理List,其中包含通过WSClient调用的请求URL,但每秒请求数不超过一个(该服务允许“每个客户端每秒只有”1个请求)。从逻辑的角度来看,这个想法是从列表中获取一个元素(URL),发出请求,然后等待一定的时间,然后再继续处理列表中的下一个元素。播放WS API:限制请求速率

  • 在像Play这样的非阻塞和异步框架中使用旧的Thread.sleep肯定是一个坏主意。
  • 对于像ScheduledThreadPoolExecutor或其他需要产生新线程的方法,情况可能也是如此。

我该如何限制请求速率,而不会对Play的异步和“尽可能小的线程”性质产生负面影响?

+0

玩什么版本的? –

+0

玩版本2.5.3 – ceran

回答

3

假设你有你看获取的URL列表:

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

在玩2.5.X,我们可以处理这些顺序,并使用akka.pattern.after迫使每个调用之间的异步延迟。我们flatMapFuture Web服务调用的结果将返回相同的值后一秒。

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范围。


在玩的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)) 
    } 
} 
1

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

由于阿卡已经在玩,你不需要输入任何东西。
这不会是最干净的或容易测试,但你可以是这样的:

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) 

或者,您也可以使用这个阿卡消息节流,有人做了一个前一阵子:http://doc.akka.io/docs/akka/snapshot/contrib/throttle.html

我已经用它之前(我认为这是去年的Akka 2.3),但不知道它是否仍然有效。