播放 WS API:限制请求率
Play WS API: throttling request rates
我正在使用异步 Play WS Scala API 来查询 RESTful 服务。我想知道如何处理一个 List
包含通过 WSClient
调用的请求 URL,但每秒不超过一个请求(该服务允许每个 "only" 1 个请求每个客户第二个)。从逻辑的角度来看,这个想法是从列表中获取一个元素 (URL),发出请求,然后等待一定的时间,然后再继续处理列表中的下一个元素。
- 在像 Play 这样的非阻塞和异步框架中使用旧的
Thread.sleep
当然不是一个好主意。
- 对于
ScheduledThreadPoolExecutor
或其他需要生成新线程的方法,情况可能也是如此。
如何在不对 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]]
这要求您有可用的 WSClient
和 ActorSystem
组件,以及范围内的隐式 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))
}
}
我正在使用异步 Play WS Scala API 来查询 RESTful 服务。我想知道如何处理一个 List
包含通过 WSClient
调用的请求 URL,但每秒不超过一个请求(该服务允许每个 "only" 1 个请求每个客户第二个)。从逻辑的角度来看,这个想法是从列表中获取一个元素 (URL),发出请求,然后等待一定的时间,然后再继续处理列表中的下一个元素。
- 在像 Play 这样的非阻塞和异步框架中使用旧的
Thread.sleep
当然不是一个好主意。 - 对于
ScheduledThreadPoolExecutor
或其他需要生成新线程的方法,情况可能也是如此。
如何在不对 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]]
这要求您有可用的 WSClient
和 ActorSystem
组件,以及范围内的隐式 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))
}
}