在最新版本的 PlayFramework 中,他们开始使用 CompletionStage
作为将用于异步执行的 Controller 的返回类型,或者简而言之,如果返回 CompletionStage
这是异步执行...
现在当我们知道我们提交给 CF
的工作时是一个长期运行的IO
操作我们需要传递一个自定义执行器(否则默认会在 FJP
上执行)。
每个 Controller 执行都有一个 HTTP
上下文中包含所有请求信息,并且此上下文对于拥有您的 EntityManagers
是必要的。如果您使用 JPA
...
如果我们只是创建自定义 ExecutorService
并将其注入(inject)我们的 Controller 中以在 supplyAsync()
中使用我们不会拥有所有上下文信息。
以下是一些 Controller 操作返回 CompletionStage
的示例
return supplyAsync(() -> {
doSomeWork();
}, executors.io); // this is a custom CachedThreadPool with daemon thread factory
}
如果我们尝试在doSomeWork()
中运行类似的东西
Request request = request(); // getting request using Controller.request()
或使用预注入(inject)的JPAAPI jpa
Controller 中的字段
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 givenExecutionContext
. The current thread's contextClassLoader
andHttp.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
。
关于java - 使用 CompletableFutures 和 java 时的 PlayFramework 自定义执行程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38195370/