Asynchronous 如何将同步控制器重写为异步控制器?
我正在使用PlayFramework2.2作为我即将推出的一个web应用程序。我已经以同步模式实现了我的控制器,并使用了几个阻塞调用(主要是数据库) 比如说, 同步版本: 因此,在优化代码的同时,决定利用异步编程支持Play。我已经阅读了文档,但是这个想法对我来说仍然很模糊,因为我对如何正确地将上面的同步代码块转换为异步代码感到困惑 因此,我提出了以下代码: 异步版本:Asynchronous 如何将同步控制器重写为异步控制器?,asynchronous,playframework,playframework-2.0,Asynchronous,Playframework,Playframework 2.0,我正在使用PlayFramework2.2作为我即将推出的一个web应用程序。我已经以同步模式实现了我的控制器,并使用了几个阻塞调用(主要是数据库) 比如说, 同步版本: 因此,在优化代码的同时,决定利用异步编程支持Play。我已经阅读了文档,但是这个想法对我来说仍然很模糊,因为我对如何正确地将上面的同步代码块转换为异步代码感到困惑 因此,我提出了以下代码: 异步版本: 公共静态承诺索引(){ 回报承诺( 新函数0(){ 公开结果应用(){ User User=db.getUser(电子邮件);
公共静态承诺索引(){
回报承诺(
新函数0(){
公开结果应用(){
User User=db.getUser(电子邮件);//阻塞
User anotherUser=db.getUser(emailTwo);//阻塞
...
user.sendmail();//对Web服务的调用,阻止。
返回ok();
}
}
);
}
因此,我只是将整个控制逻辑包装在一个promise
块中
如果您没有任何不阻塞的内容,则可能没有理由使控制器异步。这里有一个很好的博客,来自Play的创建者之一:Play框架本质上是异步的,它允许创建完全无阻塞的代码。但是为了不被阻塞——尽管它有很多好处——你不能仅仅包装你的阻塞代码,期待奇迹发生 在理想情况下,完整的应用程序是以非阻塞方式编写的。如果这是不可能的(无论出于何种原因),您可能希望在Akka actors中或在返回
scala.concurrent.Future
的异步接口后面抽象阻塞代码。这样,您可以在专用的执行上下文中(同时)执行阻塞代码,而不会影响其他操作。毕竟,让所有操作共享相同的ExecutionContext意味着它们共享相同的线程池。因此,阻止线程的操作可能会在CPU未充分利用的情况下对纯CPU的其他操作产生巨大影响
在您的情况下,您可能希望从最低级别开始。看起来数据库调用被阻塞了,所以首先重构这些调用。您需要为正在使用的任何数据库找到一个异步驱动程序,或者如果只有一个阻塞驱动程序可用,您应该在将来包装它们,以便使用特定于DB的执行上下文(使用与DB ConnectionPool大小相同的线程池)执行
在异步接口后面抽象DB调用的另一个优点是,如果在将来的某个时候,您切换到非阻塞驱动程序,您只需更改接口的实现,而无需更改控制器
在您的活动控制器中,您可以处理这些未来并使用它们(当它们完成时)。你可以找到更多有关期货的信息
下面是控制器方法的一个简化示例,该方法执行非阻塞调用,然后在您的视图中组合结果,同时异步发送电子邮件:
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));
}
公共静态承诺索引(){
scala.concurrent.Future user=db.getUser(email);//非阻塞
scala.concurrent.Future anotherUser=db.getUser(emailTwo);//非阻塞
List listOfUserFutures=new ArrayList();
添加(用户);
添加(另一个用户);
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”);
第三个(新的OnComplete(){
未完成的公共无效(可丢弃故障,用户){
user.sendmail();//对Web服务的调用,非阻塞。
}
},mailExecutionContext);
return Promise.wrap(futureListOfUsers.flatMap(newmapper()){
公共未来应用(最终可访问用户){
return Futures.future(新的Callable(){
公共结果调用(){
返回ok(…);
}
},Akka.system().dispatcher());
}
}(欧共体);
}
使用play WS库的web服务调用是非阻塞的。对于DB调用,如前一篇文章所述,您必须查看数据库服务器是否有非阻塞驱动程序。据我所知,只有MongoDB有一个非阻塞驱动程序,我正在使用MySQL和Play的EBean。所以,我想它还不支持Future?我想EBean确实支持异步查询。。。嘿,詹姆斯,你为什么说没有理由异步呢?OP要求优化,您不同意完全非阻塞和编写可重用代码是对阻塞的优化吗?我同意仅仅包装你的阻塞代码是没有意义的,但我不明白你为什么建议他保留他所拥有的…如果代码可以是非阻塞的,那么它必须是异步的。但是如果它不能是非阻塞的,那么就没有理由是异步的。Play已经在幕后异步处理了所有请求。JDBC正在阻塞,并且试图以异步方式包装它不会带来任何性能优势。在actors中加入db肯定还有其他原因,但不是为了异步。我同意。但是当问题特别指出db调用被阻塞并询问如何使控制器异步时,正确的答案是“不要”。如果问题是“如何使整个过程不阻塞?”那么答案就大不相同了。@JamesWard通过对控制器(非异步)使用经典的操作,他将不得不等待结果(<
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();
}
}
);
}
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));
}