事实上整个Play框架都是异步的。Play非阻塞地处理每一个request请求。html
默认的配置适配的正是异步的controller。所以开发者应该尽力避免在在controller中阻塞,如在controller方法中等待其余的操做。常见的例子如:JDBC调用、流式API、HTTP请求以及长时间的计算等。web
虽然能够经过提供并发线程数来容许阻塞式的controller来处理更多的请求,可是使用异步controller在高负载时的扩展性及简便性上更有优点。数据库
Play的工做模式决定了Action的速度必需要尽量的快(非阻塞)。那么当咱们尚未生成返回值时如何返回呢?其实 response 是一个 future result!后端
Future[Result] 表明将来某时刻的Result。经过返回 Future 来取消当前的阻塞,Play将会尽量快的返回真正的Result。api
虽然web客户端在等待响应的时候会阻塞,可是服务端不会有任何阻塞,资源能够获得最大程度的重用。缓存
仅仅使用 Future 不是所有!若是调用阻塞的API,例如JDBC,那么你须要在不一样的 excutor 中运行本身的 ExecutionStage,而不是使用Play的 rendering 线程池。这时你能够创造一个带 custom dispatcher 引用的 play.api.libs.concurrent.CustomExecutionContext 子类:服务器
import play.api.libs.concurrent.CustomExecutionContext trait MyExecutionContext extends ExecutionContext class MyExecutionContextImpl @Inject()(system: ActorSystem) extends CustomExecutionContext(system, "my.executor") with MyExecutionContext class HomeController @Inject()(myExecutionContext: MyExecutionContext, val controllerComponents: ControllerComponents) extends BaseController { def index = Action.async { Future { // Call some blocking API Ok("result of blocking call") }(myExecutionContext) } }
想要优化自定义的线程池能够查看 ThreadPools。网络
建立 Future[Result] 以前咱们必须首先建立另外一个Future,用它来提供计算最终Result所须要的参数:架构
val futurePIValue: Future[Double] = computePIAsynchronously() val futureResult: Future[Result] = futruePIValue.map { pi => Ok("PI value computed: " + pi) }
Play 全部的异步API调用都会返回 Future,不管你用 play.api.libs.WS 调用外部接口,或者利用Akka来调度异步任务,仍是使用 play.api.libs.Akka 来和 actores通讯。并发
下面是异步获取Future的一个代码块例子:
val futureInt: Future[Int] = scala.concurrent.Future { intensiveComputation() }
注意:理解线程返回future的部分是很重要的。上面两段代码中,默认导入了Play的execution context。它其实是以回调做为参数的 future API 接收到的一个隐式参数。此execution context常常能够等同于一个线程池,虽然这并非必须的。
你可使用Future来将同步IO简单地包装为异步。但若是你没法从application架构上调整来避免阻塞,在某一时刻它仍然会被阻塞住。想将操做完全异步化,你须要使用一个独立的execution context,并为之配置好足够的线程以应对并发需求。请查看 理解Play线程池 了解细节, play模板样例 展现了数据库集成。
Actor对于阻塞操做仍然是有意义的。它提供了整洁的方式来处理超时和异常,设置阻塞的execution context,以及管理服务相关的状态。此外,Actor还提供了ScatterGatherFirstCompletedRouter来处理同步缓存和数据库请求,并容许在后端服务器集群上远程执行代码。固然是否使用Actor要取决于你的需求,也不要过分的使用它。
目前为止咱们只用 Action.apply 构造器方法来构建action,为了发送异步result,咱们须要使用 Action.async 构造器方法:
def index = Action.async { val futureInt = scala.concurrent.Future { intensiveComputation()} futureInt.map(i => Ok("Got result: " + i)) }
Play actions 是默认异步的。例以下面的controller, { Ok(...)} 并非 controller 的方法体。它是传递到Action object的apply方法的一个匿名函数,将由它建立Action。Play内部会调用你建立的匿名函数并将返回的result封装进Future。
def echo = Action { request => Ok("Got request [" + request + "]") }
注意:不论是Action.apply仍是Action.async建立的Action其内部机制都是同样的。仅仅只有一种异步Action,而不是同步和异步两种。.async构造器仅仅是返回Future的简单方式,它让你更容易地写出非阻塞的代码。
超时处理的重要性不言而喻,它能够避免错误发生时的网络阻塞。你可使用 play.api.libs.concurrent.Futures 来包装Future,为其增长非阻塞的超时处理。
import scala.concurrent.duration._ import play.api.libs.concurrent.Futures._ def index = Action.async { // You will need an implicit Futures for withTimeout() -- you usually get // that by injecting it into your controller's constructor intensiveComputation().withTimeout(1.seconds).map { Ok("Got result: " + i) }.recover { case e: scala.concurrent.TimeoutException => InternalServerError("timeout") } }
注意:超时和取消是不一样的 —— 超时仍然属于完成(complete),即便完成的值没有被返回。