我正在将Play框架2.2用于我即将推出的Web应用程序之一。我已经以同步模式实现了我的控制器,有几个阻塞调用(主要是数据库)。
例如,
public static Result index(){
User user = db.getUser(email); // blocking
User anotherUser = db.getUser(emailTwo); // blocking
...
user.sendEmail(); // call to a webservice, blocking.
return ok();
}
因此,在优化代码的同时,决定利用Play的异步编程支持。通过文档,但这个想法对我来说仍然含糊不清,因为我对如何正确地将上述同步代码块转换为Async感到困惑。
所以,我提出了以下代码:
public static Promise<Result> index(){
return Promise.promise(
new Function0<Result>(){
public Result apply(){
User user = db.getUser(email); // blocking
User anotherUser = db.getUser(emailTwo); // blocking
...
user.sendEmail(); // call to a webservice, blocking.
return ok();
}
}
);
}
所以,我只是将整个控制逻辑包装在promise
块中。
答案 0 :(得分:6)
play框架本质上是异步的,它允许创建完全非阻塞的代码。但是为了实现无阻塞 - 带来所有好处 - 你不能只是包装你的阻止代码并期待魔法发生......
在理想情况下,您的完整应用程序是以非阻塞方式编写的。如果这是不可能的(无论出于何种原因),您可能希望在Akka actor中或在返回scala.concurrent.Future
的 async interfaces 后面抽象阻塞代码。这样,您可以在专用的执行上下文中同时执行阻止代码,而不会影响其他操作。毕竟,让所有操作共享相同的ExecutionContext意味着它们共享相同的线程池。因此,阻止线程的Action可能会严重影响其他执行纯CPU的操作,同时CPU未被充分利用!
在您的情况下,您可能希望从最低级别开始。看起来数据库调用是阻塞的,所以首先重构这些。您需要为您正在使用的任何数据库找到异步驱动程序,或者如果只有阻塞驱动程序可用,您应该在将来使用特定于DB的执行上下文执行它们(使用与该程序大小相同的ThreadPool) DB ConnectionPool)。
抽象异步接口背后的数据库调用的另一个好处是,如果将来某个时候切换到非阻塞驱动程序,您只需更改接口的实现即可无需更换控制器!
在您的重新激活的控制器中,您可以处理这些未来并与它们一起工作(当它们完成时)。您可以找到有关使用期货here
的更多信息以下是执行非阻塞调用的控制器方法的简化示例,然后在视图中组合结果,同时发送异步电子邮件:
public static Promise<Result> index(){
scala.concurrent.Future<User> user = db.getUser(email); // non-blocking
scala.concurrent.Future<User> anotherUser = db.getUser(emailTwo); // non-blocking
List<scala.concurrent.Future<User>> listOfUserFutures = new ArrayList<>();
listOfUserFutures.add(user);
listOfUserFutures.add(anotherUser);
final ExecutionContext dbExecutionContext = Akka.system().dispatchers().lookup("dbExecutionContext");
scala.concurrent.Future<Iterable<User>> futureListOfUsers = akka.dispatch.Futures.sequence(listOfUserFutures, dbExecutionContext);
final ExecutionContext mailExecutionContext = Akka.system().dispatchers().lookup("mailExecutionContext");
user.andThen(new OnComplete<User>() {
public void onComplete(Throwable failure, User user) {
user.sendEmail(); // call to a webservice, non-blocking.
}
}, mailExecutionContext);
return Promise.wrap(futureListOfUsers.flatMap(new Mapper<Iterable<User>, Future<Result>>() {
public Future<Result> apply(final Iterable<User> users) {
return Futures.future(new Callable<Result>() {
public Result call() {
return ok(...);
}
}, Akka.system().dispatcher());
}
}, ec));
}
答案 1 :(得分:2)
你没有任何东西可以阻止,然后可能没有理由让你的控制器异步。这是一个关于Play的创建者之一的好博客:http://sadache.tumblr.com/post/42351000773/async-reactive-nonblocking-threads-futures-executioncont