play框架本质上是异步的,它允许创建完全非阻塞的代码.但是为了无阻塞 - 带来所有好处 - 你不能只是包装你的阻止代码并期待魔法发生......
在理想情况下,您的完整应用程序是以非阻塞方式编写的.如果这是不可能的(无论出于何种原因),您可能希望在Akka actor中或在返回的异步接口后面抽象阻塞代码scala.concurrent.Future
.这样,您可以在专用的执行上下文中同时执行阻止代码,而不会影响其他操作.毕竟,让所有操作共享相同的ExecutionContext意味着它们共享相同的线程池.因此,阻止线程的Action可能会在CPU未充分利用的同时对其他执行纯CPU的操作产生巨大影响!
在您的情况下,您可能希望从最低级别开始.看起来数据库调用是阻塞的,所以首先重构这些.您需要为正在使用的数据库找到异步驱动程序,或者如果只有阻塞驱动程序可用,您应该在将来使用特定于DB的执行上下文(使用与DB ConnectionPool).
抽象异步接口背后的数据库调用的另一个好处是,如果在将来某个时候,您切换到非阻塞驱动程序,您只需更改接口的实现,而无需更改控制器!
在您的重新激活的控制器中,您可以处理这些未来并与它们一起工作(当它们完成时).您可以在此处找到有关与期货合作的更多信息
以下是执行非阻塞调用的控制器方法的简化示例,然后在视图中组合结果,同时发送异步电子邮件:
public static Promiseindex(){ scala.concurrent.Future user = db.getUser(email); // non-blocking scala.concurrent.Future anotherUser = db.getUser(emailTwo); // non-blocking List > listOfUserFutures = new ArrayList<>(); listOfUserFutures.add(user); listOfUserFutures.add(anotherUser); final ExecutionContext dbExecutionContext = Akka.system().dispatchers().lookup("dbExecutionContext"); scala.concurrent.Future > futureListOfUsers = akka.dispatch.Futures.sequence(listOfUserFutures, dbExecutionContext); final ExecutionContext mailExecutionContext = Akka.system().dispatchers().lookup("mailExecutionContext"); user.andThen(new OnComplete () { public void onComplete(Throwable failure, User user) { user.sendEmail(); // call to a webservice, non-blocking. } }, mailExecutionContext); return Promise.wrap(futureListOfUsers.flatMap(new Mapper , Future >() { public Future apply(final Iterable users) { return Futures.future(new Callable () { public Result call() { return ok(...); } }, Akka.system().dispatcher()); } }, ec)); }