Play Framework 2.5 JavaAsync引发CompletionException
我正在使用Play2.5构建一个简单的应用程序。为了获得更好的性能,我将Akka分块响应与Java8 CompletionStage策略结合使用。下面是生成分块响应的代码(不使用ComperableFuture时工作正常):Play Framework 2.5 JavaAsync引发CompletionException,java,playframework,playframework-2.0,sbt,Java,Playframework,Playframework 2.0,Sbt,我正在使用Play2.5构建一个简单的应用程序。为了获得更好的性能,我将Akka分块响应与Java8 CompletionStage策略结合使用。下面是生成分块响应的代码(不使用ComperableFuture时工作正常): 我没有在任何地方使用HTTP上下文,所以我不明白为什么这不起作用。使用分块响应返回正常结果时,同样的代码也在工作。请对此提供帮助在处理CompletableFuture/CompletionStage时,您必须提供HTTP执行上下文。在Scala中,上下文信息是通过隐式传递
我没有在任何地方使用HTTP上下文,所以我不明白为什么这不起作用。使用分块响应返回正常结果时,同样的代码也在工作。请对此提供帮助在处理
CompletableFuture
/CompletionStage
时,您必须提供HTTP执行上下文。在Scala中,上下文信息是通过隐式传递的,Java中不提供这些信息-这就是Play使用ThreadLocal
的原因
但是,在切换线程时可能会丢失这些信息,这就是问题的原因。您可能认为您不访问HTTP上下文,但实际上您访问了—您使用的是request()
因此,您必须更改代码,以便与执行器一起使用supplyAsync
:
由此:
CompletableFuture.supplyAsync(() -> abstractSource.getChunked(index.render(CSRF.getToken(request()).map(t ->
t.value()).orElse("no token")).body()
)
);
为此:
CompletableFuture.supplyAsync(() -> abstractSource.getChunked(index.render(CSRF.getToken(request()).map(t ->
t.value()).orElse("no token")).body()
)
, ec.current());
其中,
ec
是您的上下文:@Inject-HttpExecutionContext-ec代码>我对安东的回答进行了补充
如果您正在使用Play Java API构建一个非阻塞应用程序,每次需要调用CompletionStage
上的方法时,注入HttpExecutionContext
并传递ec.current())
可能会变得相当麻烦
为了简化工作,您可以使用decorator,它将保留调用之间的上下文
public class ContextPreservingCompletionStage<T> implements CompletionStage<T> {
private HttpExecutionContext context;
private CompletionStage<T> delegate;
public ContextPreservingCompletionStage(CompletionStage<T> delegate,
HttpExecutionContext context) {
this.delegate = delegate;
this.context = context;
}
...
}
如果您正在构建多层应用程序,并在不同的类之间传递CompletionStage
s,那么这一点尤其有用
完整的decorator实现示例。a一些答案有很好的解释……还有一件事想问一下,这是否是一种很好的设计方法?cn请分享您的观点?返回一个CompletionStage
是在Play 2.5中定义异步操作的事实标准,所以您在这里很好。关于游戏中的分块响应:看一看——通过使用Akka streams,您已经走上了一条很好的道路,所以这里也不用担心——您的设计方法很好!很高兴我能帮忙!这是归档的,这里有文档,这是一个很好的答案,但如果我正确理解您将默认播放执行器传递给CompletableFuture,这将导致阻止请求执行器,如果您在异步中执行繁重的任务
CompletableFuture.supplyAsync(() -> abstractSource.getChunked(index.render(CSRF.getToken(request()).map(t ->
t.value()).orElse("no token")).body()
)
);
CompletableFuture.supplyAsync(() -> abstractSource.getChunked(index.render(CSRF.getToken(request()).map(t ->
t.value()).orElse("no token")).body()
)
, ec.current());
public class ContextPreservingCompletionStage<T> implements CompletionStage<T> {
private HttpExecutionContext context;
private CompletionStage<T> delegate;
public ContextPreservingCompletionStage(CompletionStage<T> delegate,
HttpExecutionContext context) {
this.delegate = delegate;
this.context = context;
}
...
}
return new ContextPreservingCompletionStage<>(someCompletableFuture, context)
.thenCompose(something -> {...});
.thenApply(something -> {...});
return someCompletableFuture.thenComposeAsync(something -> {...}, context.current())
.thenApplyAsync(something -> {...}, context.current());