Handling asynchronous results
默认配置下controller即是异步的了。换言之,应用应该避免在controller中做阻塞操作,例如,让controller等待某一个操作完成。类似的阻塞操作还有:调用JDBC,流处理(streaming)API,HTTP请求和长时间的计算任务。
虽然不可能通过增加默认执行环境(execution context)中的线程数来提升阻塞controller的并发请求处理能力,但使用异步controller可以使应用更易于扩展,在负载较大的情况下依然能够保持响应。
创建非阻塞action
由于Play的异步工作方式,action应该做到尽可能的快,例如非阻塞。那么,在还没有得到返回结果的情况下,应该返回什么作为结果呢?答案是future结果!
一个Future[Result]
最终会被替换成一个类型为Result
的值。通过提供Future[Result]
而非Result
,我们能够在非阻塞的情况下快速生成结果。一旦从promise中得到了最终结果,Play就会应用该结果。
在创建一个Future[Result]
之前,我们需要先创建另一个future:这个future会返回一个真实的值,以便我们依此生成结果:
所有的Play异步API调用都会返回一个Future
。不管你是在调用外部web服务,如play.api.libs.WS
API,或是用Akka调度异步任务,亦或是使用与actor进行通讯。
以下是一个异步调用并获得一个Future
的简单例子:
注意:理解哪个线程运行了future非常重要,以上的两段代码都导入了Play的默认执行环境(execution context)。这是一个隐式(implicit)的参数,会被传入所有接受回调的future API方法中。执行环境(execution context)通常等价于线程池,但这并不是一定的。
这对于使用Actor来阻塞操作也非常有用。Actor提供了一种非常简洁的模型来处理超时和失败,设置阻塞执行环境(execution context),并且管理了该服务的一切状态。Actor还提供了像ScatterGatherFirstCompletedRouter
这样的模式来处理同时缓存和数据库请求,并且能够远程执行于后端服务器集群中。但使用actor可能有些多余,主要还是取决你想要的是什么。
返回future
迄今为止,我们一直都在调用Action.apply
方法来构建action,为了发出一个异步的结果,我们需要调用Action.async
:
Play的action默认即是异步的。比如说,在下面这个controller中,{ Ok(...) }
这部分代码并不是controller的方法体。这其实是一个传入Action
对象方法的匿名函数,用来创建一个Action
对象。运行时,你写的这个匿名函数会被调用并返回一个Future
结果。
注意:Action.apply
和Action.async
创建的Action
对象在Play内部会以同样的方式处理。他们都是异步的Action
,而不是一个同步一个异步。.async
构造器只是用来简化创建基于API并返回Future
的action,让非阻塞的代码更加容易写。