使用 CompletableFutures 和 Java 时的 PlayFramework 自定义执行器

问题描述 投票:0回答:1

在最新版本的 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
的支持,但与
CompletableFuture
不兼容(也许我错了?如果是这样的话)如何与CF一起使用?)。

java playframework concurrency completable-future
1个回答
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

© www.soinside.com 2019 - 2024. All rights reserved.