使用 CompletableFutures 和 java 时的 PlayFramework 自定义执行程序

PlayFramework custom executors when using CompletableFutures and java

在最新版本的 PlayFramework 中,他们开始使用 CompletionStage 作为将用于异步执行的控制器的 return 类型,或者简而言之,如果您 return CompletionStage是异步执行...

现在当我们知道我们提交给 CF 的工作是一个长 运行ning IO 操作时,我们需要传递一个自定义执行器(否则它将在 [=默认为 19=]。

每个控制器执行都有一个 HTTP 上下文,其中包含所有请求信息,如果您使用 JPA...

如果我们只是创建自定义 ExecutorService 并将其注入我们的控制器以在 supplyAsync() 中使用,我们将不会拥有所有上下文信息。

这是一些控制器操作的示例 returning CompletionStage

return supplyAsync(() -> {
   doSomeWork();
}, executors.io); // this is a custom CachedThreadPool with daemon thread factory

}

如果我们在 doSomeWork()

中尝试 运行 这样的事情
Request request = request(); // getting request using Controller.request()

或者在控制器中使用预注入的JPAAPI jpa字段

jpa.withTransaction(
    () -> jpa.em() // we will get an exception here although we are wrapped in a transaction
             ...
);

例外

No EntityManager bound to this thread. Try wrapping this call in JPAApi.withTransaction, or ensure that the HTTP context is setup on this thread.

如您所见,jpa 代码包含在事务中,但未找到上下文,因为这是一个纯 java 线程池。

使用 CompletableFuture 和自定义执行器时提供所有上下文信息的正确方法是什么?

我还尝试在 application.conf 中定义自定义执行程序并从 actor 系统中查找它们,但我最终会得到 MessageDispatcher,尽管它由 ExecutorService 支持,但与 CompletableFuture(也许我错了?如果是这样,如何将其与 CF 一起使用?)

您可以使用play.libs.concurrent.HttpExecution.fromThread方法:

An ExecutionContext that executes work on the given ExecutionContext. The current thread's context ClassLoader and Http.Context are captured when this method is called and preserved for all executed tasks.

所以,代码应该是这样的:

java.util.concurrent.Executor executor = getExecutorFromSomewhere();
return supplyAsync(() -> {
    doSomeWork();
}, play.libs.concurrent.HttpExecution.fromThread(executor));

或者,如果您使用 scala.concurrent.ExecutionContext:

scala.concurrent.ExecutionContext ec = getExecutorContext();
return supplyAsync(() -> {
    doSomeWork();
}, play.libs.concurrent.HttpExecution.fromThread(ec));

但我不确定是否会为 JPA 保留 EntityManager