【问题标题】:Play 2.5 preserve context in async callsPlay 2.5 在异步调用中保留上下文
【发布时间】:2016-11-17 16:44:49
【问题描述】:

在我们的控制器类中,我们联系另一个服务来获取一些数据:

Future<JsonNode> futureSite = someClient.getSite(siteId, queryParams);

return FutureConverters.toJava(futureSite).thenApplyAsync((siteJson) -> {
    Site site = Json.fromJson(siteJson, Site.class);
    try {
        return function.apply(site);
    } catch (RequestException e) {
        return e.result;
    }
}).exceptionally(throwable -> {
    if(throwable instanceof OurClientException) {
        if(((OurClientException) throwable).httpStatusCode == 404) {
           return entityNotFound("Site", siteId);
        }
    }
    return null;
});

我们注意到在单元测试中设置的上下文(我们使用 scalatest-play)在我们进行异步调用 (FutureConverters.toJava(futureSite).thenApplyAsync((siteJson) 后丢失并变为空,因为 t 在单独的线程上。

这会导致控制器代码出现问题,我们使用上述函数... request() 现在会抛出运行时异常,说明没有可用的上下文。

我们如何保存上下文?

【问题讨论】:

    标签: scala playframework scalatest playframework-2.5


    【解决方案1】:

    您应该将 play.libs.concurrent.HttpExecutionContext 注入您的控制器,然后将当前上下文指定为 CompletionStage#thenApplyAsync(..,..) 的第二个参数。

    public class Application extends Controller {
    @Inject HttpExecutionContext ec;
    
    public CompletionStage<Result> index() {
        someCompletableFuture.supplyAsync(() -> { 
          // do something with request()
        }, ec.current());
    }}
    

    附: https://www.playframework.com/documentation/2.5.x/JavaAsync#Using-CompletionStage-inside-an-Action

    【讨论】:

      【解决方案2】:

      我除了尼克的 V 回答。

      如果您正在使用 Play Java API 构建非阻塞应用程序,每次需要调用 CompletionStage 上的方法时,注入 HttpExecutionContext 并传递 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());
      

      如果您正在构建一个多层应用程序并在不同类之间传递CompletionStages,这将特别有用。

      完整的装饰器实现示例is here.

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-10-12
        • 2014-06-03
        • 2017-08-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-25
        相关资源
        最近更新 更多