Asynchronous 如何将同步控制器重写为异步控制器?

Asynchronous 如何将同步控制器重写为异步控制器?,asynchronous,playframework,playframework-2.0,Asynchronous,Playframework,Playframework 2.0,我正在使用PlayFramework2.2作为我即将推出的一个web应用程序。我已经以同步模式实现了我的控制器,并使用了几个阻塞调用(主要是数据库) 比如说, 同步版本: 因此,在优化代码的同时,决定利用异步编程支持Play。我已经阅读了文档,但是这个想法对我来说仍然很模糊,因为我对如何正确地将上面的同步代码块转换为异步代码感到困惑 因此,我提出了以下代码: 异步版本: 公共静态承诺索引(){ 回报承诺( 新函数0(){ 公开结果应用(){ User User=db.getUser(电子邮件);

我正在使用PlayFramework2.2作为我即将推出的一个web应用程序。我已经以同步模式实现了我的控制器,并使用了几个阻塞调用(主要是数据库)

比如说,

同步版本: 因此,在优化代码的同时,决定利用异步编程支持Play。我已经阅读了文档,但是这个想法对我来说仍然很模糊,因为我对如何正确地将上面的同步代码块转换为异步代码感到困惑

因此,我提出了以下代码:

异步版本:
公共静态承诺索引(){
回报承诺(
新函数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));
    }