使用 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
。
在最新版本的 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 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
。