Play Framework 异步控制器阻止对同一控制器的后续调用
Play Framework async controller blocks subsequent calls for the same controller
我的目标是从异步控制器进行一些数据库查询,然后 return 得到答案。
我正在玩这个示例项目,现在只是通过睡眠模拟数据库查询,但我注意到无论我做什么,REST 接口甚至都不会开始第二个查询的睡眠,直到第一个完成。
例如:如果我从浏览器中的一个选项卡调用 REST 接口,然后 1 秒后再次从另一个选项卡调用,我希望第二个选项卡也能在 10 秒内得到回复,但实际上是 19.
它似乎也没有使用“database-io”池:
1: application-akka.actor.default-dispatcher-2
2: application-akka.actor.default-dispatcher-5
我的代码:
@Singleton
class AsyncController @Inject()(cc: ControllerComponents, actorSystem: ActorSystem) extends AbstractController(cc) {
implicit val executionContext = actorSystem.dispatchers.lookup("database-io")
def message = Action.async {
getFutureMessage().map { msg => Ok(msg) }
}
private def getFutureMessage(): Future[String] = {
val defaultThreadPool = Thread.currentThread().getName;
println(s"""1: $defaultThreadPool""")
val promise: Promise[String] = Promise[String]()
actorSystem.scheduler.scheduleOnce(0 second) {
val blockingPool = Thread.currentThread().getName;
println(s"""2: $blockingPool""")
Thread.sleep(10000)
promise.success("Hi!")
}(actorSystem.dispatcher)
promise.future
}
}
这种行为可能有两个原因:
- 您使用的是开发模式(1线程),或者您的产品配置只配置了一个线程。
- 浏览器阻止第二个请求,直到收到第一个请求的响应。这句话:"If I call the REST interface from one tab in the browser." 尝试从不同的浏览器做同样的事情。
您需要避免阻塞代码。基本上:
你可以用一种方法 returns 未来。
你映射进去
您可以恢复 Future
结果可能带来的任何失败。
假设我有:
def userAge (userId: String): Future[Int] = ???
然后你映射进去:
userAge.map{
age => ??? //everything is ok
}.recover{ case e: Throwable => ??? //Do something when it fails
请注意,如果您有多个呼叫,另一个 map
将变为 flatMap
,因为您想要 Future[...]
而非 Future[Future[...]]
。
我的目标是从异步控制器进行一些数据库查询,然后 return 得到答案。
我正在玩这个示例项目,现在只是通过睡眠模拟数据库查询,但我注意到无论我做什么,REST 接口甚至都不会开始第二个查询的睡眠,直到第一个完成。 例如:如果我从浏览器中的一个选项卡调用 REST 接口,然后 1 秒后再次从另一个选项卡调用,我希望第二个选项卡也能在 10 秒内得到回复,但实际上是 19.
它似乎也没有使用“database-io”池:
1: application-akka.actor.default-dispatcher-2
2: application-akka.actor.default-dispatcher-5
我的代码:
@Singleton
class AsyncController @Inject()(cc: ControllerComponents, actorSystem: ActorSystem) extends AbstractController(cc) {
implicit val executionContext = actorSystem.dispatchers.lookup("database-io")
def message = Action.async {
getFutureMessage().map { msg => Ok(msg) }
}
private def getFutureMessage(): Future[String] = {
val defaultThreadPool = Thread.currentThread().getName;
println(s"""1: $defaultThreadPool""")
val promise: Promise[String] = Promise[String]()
actorSystem.scheduler.scheduleOnce(0 second) {
val blockingPool = Thread.currentThread().getName;
println(s"""2: $blockingPool""")
Thread.sleep(10000)
promise.success("Hi!")
}(actorSystem.dispatcher)
promise.future
}
}
这种行为可能有两个原因:
- 您使用的是开发模式(1线程),或者您的产品配置只配置了一个线程。
- 浏览器阻止第二个请求,直到收到第一个请求的响应。这句话:"If I call the REST interface from one tab in the browser." 尝试从不同的浏览器做同样的事情。
您需要避免阻塞代码。基本上:
你可以用一种方法 returns 未来。
你映射进去
您可以恢复
Future
结果可能带来的任何失败。
假设我有:
def userAge (userId: String): Future[Int] = ???
然后你映射进去:
userAge.map{
age => ??? //everything is ok
}.recover{ case e: Throwable => ??? //Do something when it fails
请注意,如果您有多个呼叫,另一个 map
将变为 flatMap
,因为您想要 Future[...]
而非 Future[Future[...]]
。