Java中的Play框架异步处理和阻塞I/O

14

我的应用程序使用Play框架来处理REST请求。我需要在HTTP请求处理程序中执行一些可能持续很长时间的阻塞I/O操作。同时,我希望能够有效地处理某些持续时间短暂的请求。

如此处所述:

http://www.playframework.com/documentation/2.2.0/JavaAsync

可以异步运行长时间的操作。另一方面,如此处所述:

http://www.playframework.com/documentation/2.2.x/ThreadPools

Play框架使用相同的默认线程池,在其中执行所有应用程序代码。至少在Java API中,没有可能在不同的线程池上运行异步工作。

因此,我的问题是,考虑到这样一个操作仍然使用相同的线程池,异步运行潜在的阻塞 I/O 操作是否值得。或者,增加默认线程池的大小,对于这种情况不必使用异步API是否更好?(这样代码的可读性将会大大提高)


在单独的线程池上运行长时间操作,或为每个操作启动一个单独的线程。 - Alexei Kaigorodov
@AlexeiKaigorodov:我想这么做,但是在Java API中我还没有找到这样的可能性。虽然我在Scala API中读到过这样的可能性,但是现在我正在使用Java。 - oo_olo_oo
你没有找到如何通过Java API启动线程或线程池吗? - Alexei Kaigorodov
:-) 不,我还没有找到使用原生Play框架API实现这个的方法。我可以自己做 - 这是一种解决方案。但它需要更低级别的关于Play本身的知识,才能正确地完成它。并且可能与不同的Play助手(如操作装饰器等)不兼容。 - oo_olo_oo
我不是Play的专家,但我认为启动一个线程池不会有害。但问题是,如何将长时间操作的结果传递回Play框架 - 应该使用一些异步机制,而不是在由Play控制的线程上等待结果。 - Alexei Kaigorodov
1个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
23

我建议您使用Play的F.Promise<A>将自己的上下文设置好,并在其中运行阻塞/ CPU密集型操作。与线程一样,最佳解决方案始终取决于诸多因素,如核心数量等。

首先在applications.conf中设置您的上下文:

play {
  akka {
    akka.loggers = ["akka.event.Logging$DefaultLogger", "akka.event.slf4j.Slf4jLogger"]
    loglevel = WARNING
    actor {
      default-dispatcher = {
        fork-join-executor {
          parallelism-min = 1
          parallelism-factor = 2
          parallelism-max = 6
        }
      }
      my-context {
        fork-join-executor {
          parallelism-min = 1
          parallelism-factor = 4
          parallelism-max = 16
        }
      }
    }
  }
}

然后在您的控制器中,使用 Plays Promises 利用您的上下文(我正在使用Java 8):

public static F.Promise<Result> love() {
    ExecutionContext myExecutionContext = Akka.system().dispatchers().lookup("play.akka.actor.my-context");

    F.Promise<Integer> integerPromise = F.Promise.promise(() ->
            LongRunningProcess.run(10000L)
    , myExecutionContext);

    F.Promise<Integer> integerPromise2 = F.Promise.promise(() ->
            LongRunningProcess.run(10000L)
    , myExecutionContext);

    return integerPromise.flatMap(i -> integerPromise2.map(x -> ok()));
}

这样,您的Play应用程序仍将处理default-dispatcher执行上下文中的短暂请求,并且阻塞/ CPU密集型请求将在my-context中运行。

我为您制作了一个非常简短的示例,可在Github上查看。


2
好的答案,我也发现这是正确的做法。我在默认上下文中运行时间/CPU密集型进程,这导致其他Play Futures出现问题,即Play Web Service API不再工作。将我的密集型进程移动到它们自己的上下文中解决了WS请求的问题。 - Jordan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,