使用CompletableFutures和java

时间:2016-07-05 04:50:42

标签: java playframework concurrency completable-future

在最新版本的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}不兼容(也许我错了?如果是这样的话怎么用它?)

1 个答案:

答案 0 :(得分:1)

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

  

ExecutionContext执行给定ExecutionContext的工作。调用此方法并为所有已执行的任务保留时,将捕获当前线程的上下文ClassLoaderHttp.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