gpt4 book ai didi

asynchronous - 如何在 Play 中将同步 Controller 重写为异步?

转载 作者:行者123 更新时间:2023-12-04 04:05:25 25 4
gpt4 key购买 nike

我正在将 Play framework 2.2 用于我即将推出的 Web 应用程序之一。我已经以同步模式实现了我的 Controller ,有几个阻塞调用(主要是数据库)。
例如,
同步版本:

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 中。堵塞。
  • 我的方法正确吗?
  • 我应该将 Controller 内的每个阻塞请求都转换为异步,还是将多个阻塞调用包装在单个异步 block 中就足够了?
  • 最佳答案

    Play Framework 本质上是异步的,它允许创建完全非阻塞的代码。但是为了实现非阻塞 - 以及它的所有好处 - 你不能只是包装你的阻塞代码并期望魔法发生......

    在理想情况下,您的完整应用程序是以非阻塞方式编写的。如果这是不可能的(无论出于何种原因),您可能希望在 Akka actor 中或在返回 scala.concurrent.Future 的异步接口(interface)后面抽象您的阻塞代码。的。这样,您可以在专用的执行上下文中(同时)执行您的阻塞代码,而不会影响其他操作。毕竟,让您的所有操作共享相同的 ExecutionContext 意味着它们共享相同的线程池。因此,阻塞线程的操作可能会极大地影响其他执行纯 CPU 的操作,而 CPU 未充分利用!

    在您的情况下,您可能希望从最低级别开始。看起来数据库调用被阻塞了,所以首先重构这些。您需要为正在使用的任何数据库找到异步驱动程序,或者如果只有一个阻塞驱动程序可用,您应该在将来包装它们以使用特定于 DB 的执行上下文(使用与数据库连接池)。

    在异步接口(interface)后面抽象 DB 调用的另一个优点是,如果在将来的某个时候,您切换到非阻塞驱动程序,您可以只更改接口(interface)的实现而无需更改 Controller !

    在您的响应式(Reactive) Controller 中,您可以处理这些 future 并使用它们(当它们完成时)。您可以找到更多关于使用 future 的信息 here

    这是您的 Controller 方法执行非阻塞调用的简化示例,然后在您的 View 中组合结果,同时异步发送电子邮件:

    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));
    }

    关于asynchronous - 如何在 Play 中将同步 Controller 重写为异步?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22421322/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com