在最新版本的PlayFramework中,他们开始使用CompletionStage
作为将用于异步执行的控制器的返回类型,或者简而言之,如果您返回CompletionStage
它是异步执行...
现在,当我们知道我们提交给CF
的工作是一个长期运行的IO
操作时,我们需要传递一个自定义执行程序(否则它将默认在FJP
上执行)。
每个控制器执行都有一个HTTP
上下文,其中包含所有请求信息,如果您使用EntityManagers
,则需要此上下文才能拥有JPA
...
如果我们只是创建自定义ExecutorService
并将其注入我们的控制器以在supplyAsync()
中使用,那么我们将无法获得所有上下文信息。
以下是返回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
支持但与{{1}不兼容(也许我错了?如果是这样的话怎么用它?)
答案 0 :(得分:1)
您可以使用play.libs.concurrent.HttpExecution.fromThread
方法:
ExecutionContext
执行给定ExecutionContext
的工作。调用此方法并为所有已执行的任务保留时,将捕获当前线程的上下文ClassLoader
和Http.Context
。
因此,代码将类似于:
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
。